Eu sempre me perguntei por que os processadores pararam em 32 registros. É de longe a peça mais rápida da máquina, por que não fazer processadores maiores com mais registros? Isso não significa menos ir para a RAM?
Eu sempre me perguntei por que os processadores pararam em 32 registros. É de longe a peça mais rápida da máquina, por que não fazer processadores maiores com mais registros? Isso não significa menos ir para a RAM?
Respostas:
Primeiro, nem todas as arquiteturas de processador pararam em 32 registros. Quase todas as arquiteturas RISC que possuem 32 registros expostos no conjunto de instruções realmente têm 32 registros inteiros e mais 32 registros de ponto flutuante (então 64). (O ponto flutuante "add" usa registros diferentes do número inteiro "add".) A arquitetura SPARC possui janelas de registro. No SPARC, você pode acessar apenas 32 registros inteiros de cada vez, mas os registros agem como uma pilha e você pode enviar e enviar novos registros 16 por vez. A arquitetura Itanium da HP / Intel tinha 128 registros inteiros e 128 de ponto flutuante expostos no conjunto de instruções. GPUs modernas da NVidia, AMD, Intel, ARM e Imagination Technologies, todas expõem um grande número de registros em seus arquivos de registro. (Sei que isso se aplica às arquiteturas NVidia e Intel, não estou muito familiarizado com os conjuntos de instruções AMD, ARM e Imagination, mas acho que os arquivos de registro também são grandes lá.)
Segundo, os microprocessadores mais modernos implementam a renomeação de registros para eliminar a serialização desnecessária causada pela necessidade de reutilizar recursos, para que os arquivos de registros físicos subjacentes possam ser maiores (96, 128 ou 192 em algumas máquinas). Isso (e agendamento dinâmico) elimina algumas É necessário que o compilador gere tantos nomes de registro exclusivos, enquanto ainda fornece um arquivo de registro maior ao planejador.
Há duas razões pelas quais pode ser difícil aumentar ainda mais o número de registros expostos no conjunto de instruções. Primeiro, você precisa especificar os identificadores de registro em cada instrução. 32 registradores requerem um especificador de registrador de 5 bits; portanto, instruções de 3 endereços (comuns em arquiteturas RISC) gastam 15 dos 32 bits de instrução apenas para especificar os registradores. Se você aumentasse para 6 ou 7 bits, teria menos espaço para especificar códigos de operação e constantes. GPUs e Itanium têm instruções muito maiores. Instruções maiores têm um custo: você precisa usar mais memória de instruções, para que o comportamento do cache de instruções seja menos ideal.
Apenas mais duas razões para limitar o número de registros:
Muito código possui muitos acessos à memória (30% é uma figura típica). Fora disso, normalmente cerca de 2 / 3rds são acessos de leitura e 1 / 3rds são acessos de gravação. Isso não se deve à falta de registros, ao acesso a matrizes, ao acesso a variáveis de membros de objetos etc.
Isso tem que ser feito na memória (ou no cache de dados) devido à forma como o C / C ++ é feito (tudo o que você pode obter um ponteiro precisa ter um endereço para ser potencialmente armazenado na memória). Se o compilador puder adivinhar que você não escreverá para variáveis à vontade, usando truques malucos de ponteiros indiretos, ele os colocará em registradores, e isso funciona muito bem para variáveis de função, mas não para aquelas acessíveis globalmente (geralmente, tudo o que sai do malloc ()) porque é essencialmente impossível adivinhar como o estado global mudará.
Por isso, não é comum que o compilador consiga fazer qualquer coisa com mais de 16 registros de uso geral de qualquer maneira. É por isso que todos os arquitetos populares têm tantos (o ARM tem 16).
MIPS e outros RISCs tendem a ter 32 porque não é muito difícil ter tantos registros - o custo é baixo o suficiente, então é um "por que não?". Mais de 32 é praticamente inútil e tem a desvantagem de tornar o arquivo de registro mais longo para acessar (cada duplicação no número de registros potencialmente adiciona uma camada extra de multiplexadores que adiciona um pouco mais de atraso ...). Também torna as instruções um pouco mais longas, em média - o que significa que, ao executar os tipos de programas que dependem da largura de banda da memória de instruções, seus registros extras estão na verdade diminuindo sua velocidade!
Se seu cpu está em ordem e não registra a renomeação e você está tentando fazer muitas operações por ciclo (mais de 3), em teoria você precisa de mais registros à medida que o número de operações por ciclo aumenta. É por isso que o Itanium tem tantos registros! Mas, na prática, além do código orientado a ponto flutuante numérico ou SIMD (no qual Itanium era realmente bom), a maioria dos códigos terá muitas leituras / gravações e saltos de memória, o que torna impossível esse sonho de mais de 3 ops por ciclo. (especialmente em software orientado a servidor, como bancos de dados, compiladores, execução de linguagem de alto nível como javascript, emulação etc ...). Foi isso que afundou Itanium.
Tudo se resume à diferença entre computação e execução!
Quem lhe diz que o processador sempre tem 32 registros? x86 possui 8, ARM de 32 bits e x86_64 possui 16, IA-64 possui 128 e muitos outros números. Você pode dar uma olhada aqui . Mesmo MIPS, PPC ou qualquer arquitetura que possua 32 registros de uso geral no conjunto de instruções, o número é muito maior que 32, pois sempre existem registros de sinalizador (se houver), registros de controle ... sem incluir registros e registros de hardware renomeados
Tudo tem seu preço. Quanto maior o número de registros, mais trabalho você faz ao alternar tarefas, mais espaço é necessário na codificação das instruções. Se você tiver menos registro, não precisará armazenar e restaurar muito ao chamar e retornar de funções ou alternar tarefas com o trade-off da falta de registros em algum código abrangente de computação
Além disso, quanto maior o arquivo de registro, mais caro e complexo será. A SRAM é a RAM mais rápida e mais cara, sendo usada apenas no cache da CPU. Mas ainda é muito mais barato e ocupa menos área que um arquivo de registro com a mesma capacidade.
Por exemplo, um processador Intel típico possui "oficialmente" 16 registros inteiros e 16 vetores. Mas, na realidade, há muito mais: o processador usa "renomeação de registro". Se você tiver uma instrução reg3 = reg1 + reg2, terá um problema se outra instrução usando reg3 ainda não tiver sido concluída - você não poderá executar a nova instrução caso ela substitua reg3 antes de ser lida pela instrução anterior.
Portanto, existem cerca de 160 registros reais . Portanto, a instrução simples acima é alterada para "regX = reg1 + reg2 e lembre-se de que o regX contém reg3". Sem renomear registros, a execução fora de ordem estaria absolutamente morta na água.
Eu não sou engenheiro elétrico, mas acho que outra possibilidade para limitar o número de registros é o roteamento. Há um número limitado de unidades aritméticas e elas devem poder receber entradas de todos os registradores e gerar saída para todos os registradores. Isso é especialmente verdade quando você possui programas em pipeline que podem executar muitas instruções por ciclo.
Eu tive a ideia para esta resposta assistindo algumas das palestras de Ivan Godard no CPU Mill. Parte da inovação da CPU Mill é que você não pode enviar para registros arbitrários - todas as saídas são empurradas para uma pilha de registros ou "correia", o que reduz os problemas de roteamento, porque você sempre sabe para onde a saída será. Observe que eles ainda têm o problema de roteamento para obter os registros de entrada nas unidades aritméticas.
Veja The Mill CPU Architecture - the Belt (2 de 9) para obter a declaração do problema e a solução da Mill.