Os ingredientes dos processadores modernos

Os ingredientes dos processadores modernos

Assim como em tantas outras áreas, muitos dos recursos usados nos processadores atuais surgiram muito antes, criando uma série de avanços incrementais que resultaram em processadores como o Core i7 ou o Phenom II. As bases vão mais longe do que muitos imaginam, com a maioria das funções datando da época do 486 e do Pentium. Vamos a elas:

O 486 foi o primeiro processador x86 a trazer um coprocessador aritmético. Home em dia pode parecer piada, mas até o 386 o coprocessador era um chip separado, que custava quase tanto quanto o processador. Sem ele os cálculos de ponto flutuantes precisavam ser emulados, que resultava em um desempenho incrivelmente baixo, mesmo para a época. Aqui temos uma placa de 386 com um chip 486DLC (um upgrade de baixo custo da Texas Instruments, que era popular na época), onde você pode notar o soquete vazio para a instalação do coprocessador:

archtectury_html_m793080e

O 486 foi também o primeiro chip a trazer cache integrado, com 8 KB de cache integrados diretamente no processador, complementando o cache mais lento disponível na placa-mãe. O cache interno passou a ser chamado de cache L1 e o cache da placa-mãe, de cache L2.

O cache L1 integrado se tornou um item de série em todos os processadores a partir do 486, pois melhora de forma considerável o desempenho do processador. Por oferecer tempos de latência muito baixos e ser instalado muito próximo aos registradores e às unidades de execução, o cache L1 oferece acesso quase instantâneo aos dados. Como comentei na introdução, mesmo sendo muito pequeno, o cache L1 é o responsável por cerca de 90% dos acessos.

Outra melhoria importante do 486 foi a introdução do processamento de instruções em etapas, recurso que é utilizado até os dias de hoje. A ideia central é a mesma usada nas linhas de produção: dividir o trabalho em etapas simples, que são executadas sequencialmente por unidades especializadas, cada uma capaz de executar seu trabalho em um único ciclo de clock:

archtectury_html_7edd2b08

A unidade de execução do 486 é composta por um pipeline de 5 estágios: fetch, decode, operands, execute e retire, que são os cinco passos básicos que continuam sendo usados mesmo nos processadores atuais.

Cada instrução passa sequencialmente pelos 5 estágios. O primeiro (fetch) carrega a instrução a partir do endereço correspondente do cache ou da memória, trazendo-a para os registradores do processador, onde ela pode ser processada. O segundo estágio (decode) se encarrega de decodificar a instrução, ou seja, carregar o bloco de código de máquina correspondente, que contém as operações que serão executadas.

Se o processador fosse um eletricista, a instrução poderia ser “verifique a polaridade da tomada” e o processo de decodificação consistir em consultar um manual de procedimentos em busca dos passos que devem ser executados.

O terceiro estágio (operands) tem a função de carregar os operadores, ou seja, as informações que serão processadas pela instrução. Se a instrução manda somar A+B, o terceiro estágio consistiria em carregar os valores de A e B a partir do cache.

Com todas as informações em mãos, chegamos ao quarto estágio, onde as instruções são efetivamente executadas. O conjunto de instruções x86 é bastante variado e consiste tanto em instruções simples (um único passo, como somar dois valores) e instruções complexas, que executam um conjunto de operações. O 486 é capaz de processar a maioria das instruções simples (que são de longe as mais comuns) em uma única passagem, o que garante um IPC (instruções por ciclo) quase duas vezes maior que o de um 386.

Concluindo, o último estágio (retire) se encarrega de gravar de volta o resultado das instruções depois de processadas. O 486 ainda utilizava uma arquitetura de cache write-through, onde as operações de leitura eram cacheadas, mas as escritas eram feitas diretamente na memória. A partir do Pentium os processadores passaram a utilizar cache write-back, que cacheiam também as operações de escrita, melhorando o desempenho em diversas situações.

Depois de passar pelo primeiro estágio, a primeira instrução vai para o segundo, deixando o primeiro estágio livre para carregar uma nova instrução. No ciclo seguinte, a primeira instrução passa para o terceiro estágio, e assim por diante, em um ciclo contínuo. Como as 5 unidades trabalham simultaneamente, o efeito prático é que o processador passa a executar muito mais processamento por ciclo de clock, como em uma linha de produção que passa a ter 5 trabalhadores em vez de um:

archtectury_html_m2ebf286f

O uso do pipeline trouxe outra melhoria importante, que foi o suporte a frequências de clock mais altas. O motivo é simples: com mais estágios, cada um executa menos processamento, o que permite espremer mais ciclos de processamento no mesmo espaço de tempo.

O Pentium trouxe o uso de uma arquitetura superescalar, com o uso de duas unidades de execução em vez de uma. Junto com o uso do cache, essa foi outra das grandes melhorias arquiteturais que permitiram que o desempenho dos processadores aumentasse de maneira tão surpreendente do 386 para cá.

Em vez de dependerem apenas do aumento no clock, os processadores passaram a executar mais instruções por ciclo, incorporando cada vez maior de unidades de execução e mais memória cache, sem falar nos processadores dual-core e quad-core. Isso fez com que o desempenho crescesse de forma exponencial, combinando os aumentos na frequência de operação com mais instruções processadas por ciclo. Mesmo que existisse um 486 capaz de operar a 2 ou 3 GHz, o desempenho seria 10 ou 20 vezes inferior ao de um processador atual operando na mesma frequência.

De volta ao Pentium, a primeira unidade de execução (pipe U) é uma unidade completa, capaz de processar qualquer instrução, enquanto a segunda (pipe V) é uma unidade simplificada, capaz de processar apenas instruções simples (basicamente as mesmas que no 486 podem ser executadas em um único ciclo de clock). Como as instruções complexas são mais raras e quase sempre usadas em combinação com várias instruções simples, esse design permitiu que o Pentium atingisse o objetivo de processar duas instruções por ciclo sem que fosse necessário aumentar tanto a complexidade do núcleo.

Diferente dos processadores atuais, que usam sistemas bem mais complexos de decodificação e reorganização de instruções, com o objetivo de manter as unidades de execução ocupadas na maior parte do tempo, o Pentium continua processando as instruções diretamente (como no 486) e na ordem original, mantendo a simplicidade.

Esta abordagem é chamada de processamento ordenado (in-order) e é atualmente usada em processadores de baixo consumo, como a Atom e os processadores ARM. Ela permitiu que o Pentium fosse construído com apenas 3.1 milhões de transístores (um processador atual utiliza 200 ou 300 milhões, sem contar os caches) e fosse relativamente econômico do ponto de vista do consumo elétrico.

Continuando, o uso da arquitetura superescalar trouxe um novo problema, que é a divisão do trabalho entre as unidades de execução, já que os aplicativos continuam sendo compostos por comandos sequenciais, com o uso de muitas operações de tomada de decisão, onde o processador precisa primeiro concluir a execução de uma instrução para saber qual caminho seguir e poder assim executar as seguintes.

Para solucionar o problema foi adotado o uso de um circuito de branch prediction, encarregado de dividir as instruções entre as duas unidades e antecipar o processamento de instruções, de forma a manter ambas ocupadas na maior parte do tempo.

Em uma operação de tomada de decisão (se, então, senão), por exemplo, uma das unidades de execução processaria a primeira instrução (o “se”), enquanto a outra adiantaria o processamento das instruções de um dos caminhos possíveis, escolhido pelo circuito de branch prediction com base na sua tabela interna de possibilidades.

Sempre que o caminho correto é escolhido, o processador ganha tempo (já que aproveita o trabalho executado). Por outro lado, quando o circuito de branch prediction erra a previsão, o processador precisa descartar o trabalho realizado, limpar o pipeline e começar novamente a partir do caminho correto.

O circuito de branch prediction do Pentium era relativamente simples, projetado para examinar o código em busca de algumas funções específicas e tomar decisões pré-programadas, o que resultava em um índice de acerto de 60 a 80%. Nos processadores atuais (que usam três ou mais unidades de execução e pipelines muito mais longos) o circuito de branch prediction é muito mais complexo e trabalha com índices de acerto muito mais altos, acima de 95%. Isso é necessário pois com pipelines mais longos, a penalidade pelos erros nos processadores atuais é muito maior que na época do Pentium, que usava um pipeline de apenas 5 estágios.

Outra mudança trazida pelo Pentium foi a adoção de caches separados para dados e instruções. Diferente do 486, que usava um único bloco de cache L1 para tudo, o Pentium adotou o uso de dois blocos separados de cache L1 (de 8 KB cada um, totalizando 16 KB) para dados e instruções. Isso melhorou a eficiência do cache (em relação ao cache unificado do 486), permitindo que o processador consiga acessar instruções e os dados necessários (para executá-las) simultaneamente, em vez de precisar fazer duas operações separadas.

A arquitetura do Pentium continua via até os dias de hoje na forma do Atom, que é essencialmente um Pentium modernizado, com um pipeline mais longo, suporte às instruções SSE e capaz de operar a frequências de clock mais altas.

Dentro na nomenclatura da Intel, a arquitetura do 486 é chamada de “P4” e a do Pentium é chamada de “P5”, indicando que são designs de, respectivamente, quarta e quinta geração. Em novembro de 1995, a Intel lançou a sexta geração da arquitetura (P6), na forma do Pentium Pro.

Embora tenha sido destinado exclusivamente a servidores e estações de trabalho e tenha sido produzido em pequeno volume, o Pentium Pro é importante pois inaugurou o uso da arquitetura que, com muitas atualizações, continua em uso até os dias de hoje, na forma do Core 2 e do Core i5/i7. Ironicamente, as duas tentativas de romper com a arquitetura do Pentium Pro (o Itanium e o Pentium 4) foram os dois maiores fracassos na história da Intel.

Os quatro pilares fundamentais da arquitetura P6 (justamente os traços de design que continuam em voga até os dias de hoje são:

1- Processamento de instruções fora de ordem (out-of-order), com o uso de unidades de execução capazes de processar apenas instruções simples (em oposição às unidades de uso geral do 486 e do Pentium), combinadas com o uso de decodificadores de instruções, que são capazes de quebrar as instruções complexas em sequências de instruções simples e ordenadores capazes de mudar a ordem de execução, permitindo que o processador processe mais instruções em paralelo.

2- O uso de pipelines mais longos para permitir o uso de frequências de clock mais altas (porém não tão longos a ponto de prejudicarem o desempenho), combinados com o uso de circuitos de branch prediction mais avançados, capazes de oferecer índices de acerto mais altos. O Pentium usava um pipeline de 5 estágios, o Pentium Pro usava um pipeline de 12 estágios e a maioria dos processadores modernos usam pipelines com de 14 a 21 estágios.

2- O uso de pipelines mais longos para permitir o uso de frequências de clock mais altas (porém não tão longos a ponto de prejudicarem o desempenho). O Pentium usava um pipeline de 5 estágios, o Pentium Pro usava um pipeline de 12 estágios e a maioria dos processadores modernos usam pipelines com de 14 a 21 estágios.

3- Uso de cache L2 integrado, bem mais rápido que os chips anteriormente usados na placa-mãe e ligado ao processador através de um barramento dedicado, que não concorre com o acesso à memória.

4- Suporte nativo ao uso de multiprocessamento (que na época do Pentium Pro era feito com o uso de dois ou mais processadores e hoje é feito com o uso de vários núcleos).

O uso de instruções simples é uma característica inspirada nos processadores RISC da década de 1990. A ideia básica é usar uma arquitetura simples, com unidades de execução capazes de processarem apenas algumas instruções simples, porém capazes de operar de forma mais eficiente e atingir frequências de clock mais altas. Para aplicar esta ideia aos processadores x86 (sem quebrar a compatibilidade com o conjunto de instruções), a Intel incorporou um componente adicional, o decodificador de instruções, que tem a função de quebrar as instruções complexas nas instruções simples que são processadas internamente pelo processador.

Essa uniformidade facilita o trabalho do circuito de branch prediction, que pode examinar o fluxo de instruções e melhorar assim seu índice de acerto. Outra característica é a adição de um ordenador de instruções (scheduler), que é capaz de reorganizar as instruções em uma ordem que permita às unidades de execução ficarem mais tempo ocupadas, adiantando o processamento de blocos de instruções que não dependem da conclusão de instruções anteriores para serem processados.

O circuito de branch prediction (ou branch target prediction) do Pentium Pro era ainda relativamente simples, projetado para examinar o código em busca de algumas funções específicas, tomando decisões com base em um pequeno histórico de operações e funções pré-programadas. Mesmo assim, ele era capaz de trabalhar com um índice de acerto na casa dos 90%, o que era importante, já que cada previsão errada custava mais de 10 ciclos de processamento.

Nos processadores atuais (que usam três ou mais unidades de execução e pipelines muito mais longos) o circuito de branch prediction é muito mais complexo e trabalha com índices de acerto muito mais altos, acima de 95%. Isso é necessário pois com pipelines mais longos, a penalidade pelos erros nos processadores atuais é muito maior.

A soma de todos esses circuitos adicionais torna o processador consideravelmente mais complexo (o que é um dos fatores por trás do grande consumo elétrico dos processadores x86 atuais), mas em compensação cria a infra-estrutura necessária para que o processador possa incluir mais unidades de execução e usá-las de maneira eficiente, executando mais processamento por ciclo de clock. Sem esses avanços, os processadores teriam estacionado no uso de duas unidades de execução, como no Pentium 1.

Além da nova arquitetura, o Pentium Pro adotou o uso de 256 KB de cache L2 full-speed (operando à mesma frequência do processador), abandonando o uso do lento cache L2 integrado à placa-mãe. Este foi outro recurso que se tornou norma em processadores de todos os fabricantes.

Examinando a arquitetura de qualquer processador atual, você notará a presença desse mesmo conjunto de recursos básicos. Os processadores incorporaram mais unidades de execução, vários núcleos, controladores de memória integrados e grandes caches L3, mas a base de todos os designs continuam sendo as mesmas.

archtectury_html_m302cdb27

Sobre o Autor

Redes Sociais:

Deixe seu comentário

X