Por que um processador possui 32 registros?


52

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?


2
Acho que além de um certo ponto, todas as suas variáveis ​​locais se encaixam nos registros. Os dados reais que você está trabalhando é provavelmente muito grande de qualquer maneira
Niklas B.

14
Rendimentos decrescentes. Claramente, os registros são "mais caros" (em vários sentidos) do que a RAM ou teríamos apenas 8 GB de registros.
David Richerby

5
Uma das razões pela qual é tão rápido é porque não existem muitos deles.
stackErr

5
Há uma diferença entre quantos registros a CPU possui no total e quantos você pode usar ao mesmo tempo.
Thorbjørn Ravn Andersen 14/03

CPUs e GPUs ocultam a latência principalmente por caches e multithreading, respectivamente. Portanto, as CPUs possuem poucos registros, enquanto as GPUs têm dezenas de milhares em registros. Veja meu documento de pesquisa no arquivo de registro da GPU, que discute todas essas compensações e fatores.
user984260

Respostas:


82

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.

nO(n)


11
Eu mencionaria os 256 FPRs do SPARC64 VIIIfx e os 32 GPRs extras que não são de janela, conseguidos adicionando uma instrução Set XAR que fornece 13 bits cada para as próximas uma ou duas instruções. Como o HPC era direcionado, a contagem de registros é mais compreensível. Eu também ficaria tentado a expor algumas das compensações e técnicas associadas a mais registros; mas você mostrou a sabedoria de evitar uma resposta mais exaustiva (e mesmo assim não exaustiva).
Paul A. Clayton

2
Adicionar um pouco do benefício cada vez menor de mais registros para código de "uso geral" pode valer a pena, embora não seja fácil encontrar medições significativas. Acho que Mitch Alsup mencionou no comp.arch que estender x86 para 32 registros em vez de 16 teria ganho cerca de 3% em desempenho em comparação com (ISTR) 10-15% para a extensão de 8 a 16 que foi escolhida. Mesmo para um ISA de armazenamento de carga, a passagem para 64 provavelmente oferece pouco benefício (pelo menos para o código GP atual). (BTW, GPUs, geralmente, têm registros em todo tópicos: por exemplo, um segmento com 250 deixando em 16 privada total para outros tópicos.)
Paul A. Clayton

É interessante ver que o gerenciamento do ambiente (portanto a conversão alfa), geralmente associado a idiomas de alto nível, é realmente usado no nível do registro.
babou

@ PaulA.Clayton Eu sempre pensei que IA-64 é a arquitetura que possui o maior número de registros ISA
phuclv

@ LưuVĩnhPhúc O SPARC64 VIIIfx era específico do HPC. Para sua informação, o Am29k (introduzido por volta de 1987-8 ) tinha 64 GPRs globais e 128 com janelas, o que é mais GPRs do que o Itanium (que possui 8 registros de ramificação e um registro de contagem de loop cuja função estaria em GPRs em alguns outros ISAs).
Paul A. Clayton

16

Apenas mais duas razões para limitar o número de registros:

  • Pouco ganho é esperado: CPU, como os atuais modelos Intel / AMD x64, tem 32kByte e mais de cache L1-D, e o acesso ao cache L1 geralmente leva apenas um ciclo de clock (comparado a cerca de cem ciclos de clock para uma única RAM completa Acesso). Portanto, há pouco a ganhar com mais dados nos registros do que com os dados no cache L1
  • Custos computacionais adicionais: ter mais registros cria uma sobrecarga que pode realmente tornar o computador mais lento:
    • Em ambientes de multitarefa, uma alternância de tarefas geralmente precisa salvar o conteúdo de todos os registros do processo deixado na memória e carregar os do processo a ser inserido. Quanto mais registros você tiver, mais tempo leva.
    • Da mesma forma, em arquiteturas sem janelas de registro, as chamadas de função em cascata usam o mesmo conjunto de registros. Portanto, uma função A que chama uma função B usa o mesmo conjunto de registros que o próprio B. Portanto, B precisa salvar o conteúdo de todos os registros que usa (que ainda mantêm os valores de A) e escrevê-los novamente antes de retornar (em algumas convenções de chamada, é tarefa de A salvar o conteúdo do registro antes de chamar B, mas o sobrecarga é semelhante). Quanto mais registros você tiver, mais demorará essa economia e, portanto, mais cara será a chamada de uma função.

Como isso funciona para o cache L1, para que não tenhamos o mesmo problema dos registros?
babou

4
Nos processadores de alto desempenho, a latência L1 Dcache é mais tipicamente de 3 ou 4 ciclos (incluindo geração de endereço), por exemplo, o Haswell da Intel possui latência de 4 ciclos (não ter uma latência de registro de dependência de dados também é mais fácil de esconder no pipeline). O Dcache também tende a suportar menos acessos por ciclo (por exemplo, 2 leituras, 1 gravação para Haswell) do que um arquivo de registro (por exemplo, 4 leituras, 6 gravações para Alpha 21264 que replicou o arquivo, 2 arquivos com 4 leituras são mais rápidos que 1 com 8)
Paul A. Clayton

@ PaulA.Clayton: Se o cache L1 tiver uma latência de 3-4 ciclos, isso sugeriria que haveria algum benefício em ter, por exemplo, alguns conjuntos de 64 palavras de memória de ciclo único com seu próprio espaço de endereço de 64 palavras e instruções dedicadas de "carregamento / armazenamento direto", especialmente se houvesse uma maneira de enviar todos os valores diferentes de zero, seguidos por uma palavra dizendo quais palavras eram diferentes de zero e, em seguida, uma maneira de retorná-las (zerando os registros que não aparecerem) . Muitos métodos têm entre 16 e 60 palavras de variáveis ​​locais; portanto, reduzir o tempo de acesso para aqueles de 3 a 4 ciclos a um seria útil.
Supercat

@supercat Várias idéias de cache de pilha (e global / TLS [por exemplo, Knapsack]) foram apresentadas em trabalhos acadêmicos, bem como mecanismos como o buffer de assinatura ( PDF ) Uso real, nem tanto (ao que parece). Isso está ficando tagarelado (provavelmente deve terminar ou ir para outro lugar).
Paul A. Clayton

4

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!


2

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.


2

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.


1

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.

O(n2)

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.


"Eles devem ser capazes de receber entradas de todos os registradores e enviar para todos os registradores". - Eu esperaria que isso normalmente seja implementado com um barramento, não precisa haver uma conexão separada com a (s) ALU (s) para cada registro.
user253751

11
@immibis: se você quiser mover dados em 300 picossegundos, um ônibus não fará isso. E se você deseja mover muitos dados (por exemplo, para executar três instruções com dois operandos e um resultado cada no mesmo ciclo), um barramento não funcionará absolutamente, absolutamente.
gnasher729

0

Quanto ao MIPS ISA, Hennessy e Patterson, Organização e Design de Computadores , 4ª edição, p. 176, responde diretamente a essa pergunta específica:

Menor é mais rápido. O desejo de velocidade é o motivo pelo qual o MIPS possui 32 registros em vez de muitos outros.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.