Cache de Memória

Caches de memória embutudas na Placa Mãe

Cache de memória L2 interna ao Processador

É um sistema que utiliza uma pequena quantidade de memória estática como intermediária no acesso à lenta memória RAM (se comparada com a velocidade de trabalho dos processadores). A fim de aumentar o desempenho desse circuito, os fabricantes passaram a embutir, em muitos casos, atualmente, o cache de memória dentro do próprio processador.

Embora o seu funcionamento varie de acordo com o método organizacional empregado pelo controlador de cache, a finalidade é a mesma: aumentar o desempenho do micro, fazendo com que wait states não sejam necessários. Nesse contexto, o controlador de cache tentará entregar ao processador dados sem que este necessite buscá-los na memória RAM, que é lenta. Quando isso ocorre, dizemos que houve um acerto (hit). A taxa de acerto típica varia entre 80% e 99%, ou seja, na pior das hipóteses, em 80% das vezes, o acesso à memória RAM é feito com o intermédio do cache de memória.

Outrora, quando o processador necessita de um dado que não está no cache, tem de buscá-lo diretamente na memória RAM, utilizando wait states e baixando o desempenho do micro. Quando isso ocorre, dizemos que houve um erro (miss). A taxa de erro típica varia entre 1% e 20%.

Mister lembrar que todos os processadores atualmente trabalham com o esquema de multiplicação de clock. Por conta disso, o acesso à memória RAM é muito mais lento do que o acesso a qualquer circuito interno do processador. Nos processadores atuais, onde o cache L2 está dentro do próprio processador, o desempenho é muito maior quando há um acerto do controlador de cache: o processador acessa os dados do cache L2 na sua freqüência de operação interna. Quando há um erro, o processador precisa buscar os dados na memória RAM, caminho este que é muito mais lento.

Por exemplo, em processadores com frequencia de 800MHz (valor aumentado pelo sistema de multiplicação de clock), os dados podem ser buscados do cache L2 a 800 MHz (taxa de transferência de 6.400 MB/s), mas se o processador tiver de buscá-los na memória RAM, a freqüência a ser usada passa a ser 100 MHz (taxa de transferência de 800 MB/s).

Cache L1

cache de Memória

Os processadores a partir do 486 passaram a ter uma pequena quantidade de memória estática dentro do próprio processador. Neste processador, o cache de memória L1 era unificado, isto é, servia tanto para armazenar dados quanto instruções. A partir dos processadores Intel de 5ª geração, o cache de memória L1 passou a ser dividido em dois, um para instruções e outro para dados, o que fez aumentar o desempenho desse circuito.

Na época do 486 e do Pentium, o cache L1 também era chamado de cache interno, porque o cache de memória L2 estava localizado na placa-mãe e era, portanto, externo ao processador. Atualmente os processadores têm tanto o cache L1 quanto o cache L2 dentro do próprio processador, portanto as classificações interno e externo não fazem mais sentido.

ObS: O proecssador Interl Core i7 possui o Cache L3 embutido, além do Cache L2, dentro do própio processador, sendo que os quatro núcleos compartilham o mesmo cache L3.

Para entender o funcionamento do cache L1 teremos de recordar um pouco como os processadores funcionam internamente. O circuito responsável por carregar as instruções da memória RAM para a execução dentro do processador chama-se unidade de busca. Na época em que não havia o cache de memória L1 (até os processadores 386), a unidade de buscar tinha de pegar instruções diretamente da memória RAM. A partir do 486, a unidade de busca do processador passou a buscar as instruções no cache de memória L1. O circuito controlador de cache L1 tentava “adivinhar” quais seriam as próximas instruções que o processador precisaria, carregando-as da memória RAM para o cache L1 antecipadamente (na realidade, primeiro o processador tenta buscar da memória cache L2; o controlador de cache L1 só precisa buscar dados da memória RAM caso o dado que se pretenda carregar não esteja na memória cache L2).

Essa adivinhação não tinha mistério algum: a maioria dos programas são executados de forma seqüencial. Assim, supondo que o processador está executando a instrução do endereço 1, provavelmente a próxima instrução que o processador irá precisar será a instrução que está armazenada no endereço 2, e depois a instrução armazenada no endereço 3 e assim sucessivamente (ver Figura abaixo). Dessa forma, quando a unidade de busca pede o endereço 1, o controlador de cache carrega antecipadamente um bloco de endereços seqüenciais ao endereço que o processador pediu para dentro do cache L1 de instruções. Como os programas normalmente são seqüenciais, quando o processador pedir o endereço 2, este já estará dentro do cache L1 de instruções, fazendo com que o processador não precise buscá-lo na memória RAM (ou na memória cache L2).

Programas normalmente são executados de forma seqüencial

Obs: trata-se de exemplos hipotéticos de endereçamento, considerando que cada instrução ocupa apenas endereço. Se você está acostumado com linguagem Assembly, isso pode parecer uma heresia, já que as instruções x86 podem ocupar mais de um endereço (já que não têm comprimento padronizado). Porém, para fins didáticos, estamos assumindo que todas as instruções têm o mesmo tamanho, para facilitaras nossas explicações.

Supondo um processador com cache L1 de instruções de 16 KB, quando o processador pede o primeiro endereço, o controlador de cache L1 carrega logo os 16 KB seguintes a esse endereço para dentro do cache L1 de instruções. Na prática, não é assim que ocorre, o cache L1 de instruções é dividido em unidades menores, mas essa explicação simplista e  “errada” torna o entendimento do funcionamento do cache L1 muito mais simples e didática.

O cache L1 tem um tamanho limitado e bem pequeno,  o controlador de cache percebe que o processador está chegando ao fim do cache e carrega os próximos 16 KB da memória para dentro do cache L1 de instruções assim que o processador pedir a última instrução presente no cache.

Esse esquema funcionaria muito bem se não fosse o fatos de nem sempre os programas são executados de forma seqüencial, podem existir desvios, isto é, instruções que mandam o processador carregar outras instruções a partir de um outro endereço.

Se o desvio for para uma porção de programa já carregado no cache L1 de instruções não há qualquer perda de desempenho — afinal, a instrução que o processador irá precisar já está carregada no cache L1.

Exemplo de um desvio que aponta para uma porção de programa já carregada no cache de memória

Se o desvio for para uma porção de programa ainda não carregado no cache L1 de instruções, dizemos que houve um erro (miss) e o processador terá de ir na memória RAM buscar o conteúdo desse endereço (isto é, a instrução que está lá armazenada).

Exemplo de um desvio que aponta para uma porção de programa não carregada no cache de memória

O cache L2 funciona justamente fornecendo um caminho de dados mais rápido para o cache L1. Antes de ir na memória RAM (que é lenta, não se esqueça que os processadores atuais trabalham com um clock interno muito maior do que o clock externo), o controlador de cache L1 verifica se a instrução que o processador está pedindo já não foi carregada para dentro do cache L2. O cache L2 é muito maior do que o cache L1 e, portanto, armazena mais informações. No caso de processadores onde o cache L2 está localizado na placa-mãe (386, 486, Pentium e demais processadores soquete 7), o processador precisará buscar os dados usando a freqüência de operação externa do processador, porém o cache L2 é capaz de ser acessado pelo processador sem o uso de wait states. No caso de processadores com o cache L2 integrado dentro do próprio processador, esse circuito será acessado na mesma freqüência de operação interna do processador, não penalizando o processador no caso de a instrução requerida pelo processador não estar no cache L1 de instruções.

Entretanto, se a instrução que o processador está querendo também não estiver no cache de memória L2 (ou seja, houve um erro do cache L2), então o processador terá de ir à memória RAM buscar essa instrução.

Obs: A fim de aumentar o desempenho do processador, o circuito controlador de cache passou a ter um circuito de previsão de desvio. O controlador de cache do 486 já tinha um circuito rudimentar de previsão de desvio, mas foi a partir dos processadores Intel de 5ª geração que essa situação se tornou mais eficiente.

O tipo de desvio que demos como exemplo nas figuras acima é um desvio incondicional. Esse tipo de desvio é obtido por instruções do tipo JMP (jump), ou seja, “vá para”. Ao receber uma instrução desse tipo, o processador é redirecionado a carregar a próxima instrução do endereço informado por essa instrução. Então, basta o circuito de previsão de desvio procurar dentro das instruções carregadas dentro do cache L1 por instruções desse tipo e mandar o circuito controlador de cache carregar antecipadamente o conteúdo do endereço para onde o processador irá futuramente ser desviado para dentro do cache L1 de instruções. Assim, no exemplo da figura imediatamente acima, o circuito de previsão de desvio detectaria automaticamente a instrução de desvio incondicional e carregaria antes de o processador atingir o desvio o conteúdo do endereço 100.000 bem como uma pequena porção do programa seqüencial a esse endereço. Assim, quando houver o desvio, haverá um acerto do circuito de cache de memória e não mais um erro, isto é, a instrução solicitada estará dentro do cache de memória.

O problema ocorre no caso de desvios condicionais. Desvios condicionais dependem de uma comparação para poderem ser decididos. Vemos um exemplo desse tipo de desvio na figura abaixo. Digamos que a instrução 3 do programa é “compare A com B” e a instrução 4 do programa é “se A for maior ou igual a B, vá para o endereço 100.000”. Se o programa estiver ainda executando a instrução 1, o circuito de previsão de desvio, a princípio, não tem como saber para qual endereço o programa seguirá dali por diante, já que os valores de A e B podem ser alterados na instrução imediatamente anterior da comparação ser feita.

Exemplo de desvio condicional

A princípio, uma condição como essa poderia fazer com que houvesse um erro do cache, já que o processador não tem como saber para qual endereço o programa vai daquela instrução em diante; tem de esperar o programa chegar naquele ponto, resolver a comparação e então pedir para um endereço ser carregado, o que pode gerar um acerto, caso o endereço do desvio já esteja carregado no cache, ou um erro, em caso negativo (no exemplo, se o programa fosse desviado para o endereço 100.000).

O circuito de previsão de desvio simplesmente carrega para dentro do cache de memória as duas ramificações possíveis. Com isso, a chance de acerto do cache é de 100%. Quando o programa chegar na decisão do desvio, independentemente de qual caminho tenha sido escolhido pelo programa, a próxima instrução já estará dentro do cache. O circuito de previsão de desvio irá descartar do cache de memória a ramificação que não foi usada, após o programa ter passado pelo ponto de decisão.

O processador possui três pequenas memórias dentro dele para o seu circuito de previsão de desvio: BHT (Branch History Table, tabela com histórico dos desvios), BTB (Branch Target Buffer, buffer de destino do desvio) e RAS (Return Address Stack, pilha de endereços de retorno). Quanto maior for essas memórias usadas para o controle do circuito de previsão de desvio, mais eficiente será o circuito de previsão de desvio e, consequentemente, melhor o desempenho do processador.

A pilha de endereços de retorno serve para armazenar o endereço de origem de instruções do tipo CALL, usado para chamadas de subrotina. Esse tipo de instrução é um desvio incondicional para um endereço, assim como as instruções do tipo JMP, porém o programa tem de voltar ao endereço que chamou a subrotina ao encontrar uma instrução RET. Isso pode ser visto na figura abaixo. Com o uso de uma pilha (uma pequena memória) contendo os endereços de chamadas de subrotina, o processador tem como saber de imediato para qual endereço tem que voltar quando encontrar uma instrução RET.

Chamada a uma subrotina

O cache L1 de dados, por sua vez, tem um papel diferente. Do ponto de vista do cache de memória L1 de instruções, quando dizemos instruções, significa instruções que podem ou não ter dados acoplados. Por exemplo, a instrução MOV AX, FFFFh é uma instrução que faz com que o valor FFFF seja carregado dentro do registrador AX do processador. FFFFh é um dado para a instrução MOV AX, porém para o cache L1 o dado FFFFh é encarado como parte da instrução MOV AX. Assim, à instrução MOV AX, FFFFh é encarada como uma instrução e não como uma instrução e um dado.

Dessa forma, do ponto de vista do cache de memória, instrução significa “tudo aquilo que é carregado para dentro do processador”.

Entretanto, existem instruções que podem buscar dados de endereços da memória RAM ou então armazenar dados em endereços da memória RAM (não necessariamente da memória RAM do micro, pode ser da memória da placa de vídeo, que pode ser acessada diretamente pelo processador, já que essa memória está endereçada em uma área que o processador é capaz de acessar; estamos dizendo memória RAM apenas para facilitar o entendimento).

Uma instrução como MOV [2000h], AX faz com que o conteúdo do registrador AX seja armazenado no endereço 2000h. Internamente para executar essa instrução, o processador precisa executar duas tarefas. Primeiro, saber qual é o valor do dado armazenado no registrador AX. E, em seguida, armazenar esse dado no endereço 2000h. No caso de processadores com cache de memória L1 dividido, esse pedido será enviado para o cache L1 de dados, através da unidade de armazenamento do processador. Isso faz com que o processador seja “liberado”, enquanto o circuito controlador de cache L1 trata de atualizar a memória RAM com o dado que foi pedido para ser armazenado.

Se não houvesse cache de memória L1, todas as operações de escrita em memória teriam de ser feitas diretamente, isto é, o processador teria de armazenar o dado diretamente na RAM e não no cache (já que esse circuito não existiria). O ponto é que o processador acessa a memória RAM através de seu clock externo. Em um processador de 800 MHz, por exemplo, o processador teria de enviar o dado para a memória RAM a 100 MHz, esperar esse dado ser armazenado, para então ficar liberado para efetuar a próxima tarefa. Com o uso do cache L1 de dados, o processador envia o dado para o cache L1 a 800 MHz, fica logo liberado e, enquanto o processador está efetuando outras tarefas, o controlador de cache está atualizando a RAM.

O processo de leitura em memória é análogo, só que efetuado pela unidade de carga do processador. Nesse caso, o interessante é que, se o endereço já foi lido recentemente, o seu conteúdo ainda se encontra no cache L1 de dados e, assim, o controlador de cache não precisa ir na memória RAM buscá-lo. No caso de o dado solicitado não estar no cache L1, o controlador de cache irá procurá-lo no cache L2. Se também não tiver sido carregado antecipadamente no cache L2, então o dado terá de ser buscado da RAM.

A unidade de carga e a unidade de armazenamento também são conhecidas como unidade de geração de endereços. Como estamos vendo, a finalidade dessas unidades é enviar o endereço de um dado a ser armazenado ou carregado para o cache L1 de dados. No caso do armazenamento de um dado, além do endereço é necessário que o processador forneça o dado a ser armazenado.

Obs: Os processadores Intel de 7ª geração (Pentium 4) têm uma arquitetura de cache L1 completamente diferente da explicada. Esses processadores simplesmente não têm cache L1 de instruções. A unidade de busca pega as instruções diretamente do cache L2 do processador. Em compensação, esse processador tem um cache de execução, de aproximadamente 150 KB, localizado entre o decodificador CISC/RISC e unidades de execução. Esse cache de execução armazena microinstruções RISC já decodificadas. O cache L1 de dados desses processadores é de apenas 8 KB, mas em compensação está conectado ao cache L2 através de um caminho de 256 bits, um caminho quatro vezes maior do que o usado por todos os outros processadores existentes, permitindo um desempenho quatro vezes maior. Por exemplo, em um Pentium III de 1 GHz os dados entre o cache L2 e o cache L1 de dados são transferidos a 8.000 MB/s. Em um Pentium 4 de 1 GHz, os dados entre o cache L2 e o cache L1 de dados são transferidos a 32.000 MB/s.

O cache de memória L1 é usado também para o armazenamento do cache de página (TLB). O cache de página é uma tabela contendo as últimas conversões de endereços lineares em endereços físicos.

O cache L1 de instruções tem uma função completamente diferente do cache L1 de dados, apesar de normalmente somarmos o valor dos dois e dizermos que o processador x tem y KB de memória cache L1. O cache dividido aumenta o desempenho do processador por inúmeros motivos que devem ter ficado claros. De uma maneira bastante genérica, podemos dizer que o cache unificado tem como desvantagem armazenar tanto informações que estão entrando para o processador (instruções) como informações que estão entrando para completar o processamento (dados que estavam armazenados em memória e que foram requisitados por instruções) e que estão saindo do processamento para a memória. O controle do cache unificado é muito mais complexo. Além disso, não podemos nos esquecer que o cache L1 é uma memória como qualquer outra e tem uma característica inerente às memórias: somente um dispositivo pode acessá-lo por vez. No caso do cache dividido, além do controle ser muito mais simples, os circuitos podem ser acessados simultaneamente, já que são circuitos independentes.

Cache L2

O cache L1 tem papel primordial no desempenho do micro.  No caso do conteúdo de um endereço solicitado (uma instrução) não estar carregado dentro do cache L1 de instruções, houve um erro, e o circuito controlador de cache L1 irá buscar a instrução solicitada pelo processador diretamente da memória RAM, verificando, antes, se essa instrução já não foi carregada para o cache L2.

Enquanto o cache L1 é pequeno e agiliza o carregamento das instruções para a entrada no processador, o cache L2 é normalmente grande e serve para carregar blocos grandes de dados da memória RAM.

Até a época dos processadores soquete 7 (que não são mais fabricados), o cache de memória L2 era localizado na placa-mãe do micro, sendo acessada na freqüência de operação externa do processador. A partir dos processadores Intel de 6ª geração, o cache L2 passou a estar integrado dentro do próprio processador, o que aumentou muito o desempenho no acesso a esse circuito.

O cache L2 também era chamado, antigamente, de cache externo, porém atualmente essa classificação não faz mais sentido, já que tanto o cache L1 quanto o cache L2 estão embutidos dentro do processador, bem como alguns processadores atual também trazem o Cache L3 imbutido, como nos casos do Interl Core i7 e AMD Barcelona, este com quatro núcleos e cache L3 compartilhado.

Assim como o cache L1, o cache L2 tenta antecipar os próximos endereços a serem lidos pelo processador, colocando no cache de memória os dados lá contidos. O acesso ao cache de memória é feito sem a utilização de wait states e, no caso dos processadores onde o cache L2 é embutido dentro do processador, sem a necessidade de usar o clock externo para acessar o conteúdo da memória RAM.

Na maioria das vezes, o processador busca dados não da memória RAM, mas sim a cópia contida no cache de memória. Na pior das hipóteses, como vimos, em 80% das leituras que o processador faz à memória RAM, busca dados contidos no cache. Se precisasse ler a memória RAM, diminuiria o desempenho do micro por causa da utilização de wait states, por causa da latência da memória (tempo demorado para a memória devolver dados solicitados) ou ainda por causa da velocidade do barramento, que é muito inferior à velocidade interna do processador.

Em casos onde o cache de memória L2 era externo, havia a necessidade de se utilizar um circuito controlador de cache, esse componente estava embutido no chipset da placa-mãe, em um circuito chamado Ponte Norte.

O desempenho do cache de memória L2 dependia de alguns fatores, especialmente o seu tamanho, a arquitetura e a tecnologia utilizadas nos circuitos de memória cache. Em casos onde o cache L2 era externo, a arquitetura depende sobretudo do controlador de cache, isto é, do chipset da placa-mãe. Por esse motivo, podemos ter diferença de desempenho dependendo do chipset utilizado.

Quando do cache L2 externo, a quantidade máxima de cache L2 que um micro pode ter também depende do chipset da placa-mãe. Por exemplo, os chipsets Intel para soquete 7 aceitam, no máximo 512 KB de cache L2, enquanto os chipsets da VIA Technologies aceitam 2 MB de cache. Além disso, sendo externo, algumas vezes havia como aumentar o cache de memória L2 do micro, fazendo com que o desempenho aumente.

Já em processadores onde o cache L2 é integrado ao processador, havia a vantagem do processador conseguir fazer um acesso ao cache L2 muito mais rapidamente, pois o barramento externo do processador é muito mais lento. Integrado ao processador, não há como aumentarmos a quantidade de cache L2 do micro, somente trocando o processador.

Acesso ao cache em processadores onde o cache L2 é externo

Na Figura acima, nós vemos como é feito o acesso ao cache L2 em processadores onde esse componente é externo. A freqüência de operação usada é a freqüência de operação do barramento local, que é tipicamente de 66 MHz. Processadores da AMD como alguns modelos de K6-2 e o K6-III utilizam barramento externo de 100 MHz, o que aumentava o desempenho no acesso ao cache de memória.

Outro problema dessa arquitetura é que o cache L2 utiliza um barramento concorrente, isto é, o mesmo caminho que é usado para acessar o cache L2 é usado para acessar a memória RAM. Isso significa que se o processador quiser acessar ao mesmo tempo o cache de memória e a memória RAM, não podia.

Já em processadores que têm o cache de memória L2 embutido dentro do próprio processador o cache L2 é acessado na mesma freqüência de operação interna do processador. Na figura abaixo, nós exemplificamos o caso de um Pentium III-800 FC-PGA, onde o cache L2 é acessado a 800 MHz e o barramento externo, a 100 MHz.

Acesso ao cache em processadores onde o cache L2 é embutido

Nesse caso, além de o cache L2 ser acessado na mesma freqüência de operação interna do processador, o acesso ao cache utiliza um barramento independente, isto é, o processador pode acessar ao mesmo tempo o cache L2 e a memória RAM, já que o cache L2 e a memória RAM utilizam barramentos diferentes.

Obs: Em alguns processadores, como o Pentium II, alguns modelos de Pentium III SECC-2 e alguns modelos de Athlon slot A, o cache de memória L2 não está localizado dentro do processador, mas sim sobre a placa onde o processador é soldado, dentro de um cartucho. Neste caso, o cache L2 é acessado na metade da freqüência de operação do processador, conforme visto na figura abaixo.

Funcionamento do cache de memória L2 do Pentium II, alguns modelos do Pentium III e alguns modelos do Athlon

Reiteramos que o desempenho do cache L2 depende de sua quantidade, freqüência em que é acessado e arquitetura.

Cache L3

A relação entre o cache L2 e o cache L3 é similar à relação entre o cache L1 e o cache L2. Se os dados solicitados não estiverem no cache L2, o controlador de cache irá procurá-los no cache L3. Não encontrando no cache L3 os dados, eles terão de ser buscados diretamente da memória RAM.

A divisão tradicional entre cache L1 e cache L2 funcionou bem durante a fase dos processadores single-core e dual-core. Entretanto, com a introdução dos processadores quad-core passou a fazer mais sentido usar caches L1 e L2 menores e incluir um terceiro nível de cache. Com isso, temos 4 pequenos blocos de cache L1 e L2 (um para cada núcleo) e um grande cache L3 compartilhado entre todos.

Um bom exemplo é o Core i7 de 45 nm, que usa 64 KB de cache L1 e 256 KB de cache L2 por núcleo e usa um grande cache L3 de 8 MB compartilhado entre todos. Dentro do processador, ele corresponde à área sombreada no diagrama a seguir, novamente uma área considerável.

Em micros antigos os caches se limitavam a armazenar as últimas informações acessadas, guardando cópias de dados acessados pelo processador e descartando as informações mais antigas ou menos acessadas. Os cache atuais são bem mais eficientes, incorporando algoritmos bem mais eficientes e sistemas de prefetch, que monitoram o fluxo de instruções e carregam antecipadamente dados que serão necessários nos ciclos seguintes. Desde o Pentium, o cache é também capaz de acelerar as operações de gravação, permitindo que o processador grave os dados diretamente no cache, deixando que o controlador se encarregue de gravá-los na memória posteriormente.

Cache L4

Os processadores Intel IA-64 permitem ainda a utilização de um cache L4 na placa-mãe. A utilização desse cache depende do fabricante da placa-mãe. A sua quantidade máxima e arquitetura depende do chipset da placa-mãe. A relação entre o cache L4 e L3 é a mesma existente entre o cache L1 e L2. Caso um dado solicitado não esteja no cache L3, o controlador de cache o procurará no cache L4. Não estando nesse cache, o controlador terá de buscar o dado diretamente na memória RAM.

Modo BURST

Para aumentar ainda mais a velocidade de acesso à memória, o cache de memória trabalha em um modo chamado burst (rajada). Nesse modo, o processador busca automaticamente no cache uma quantidade de dados do tamanho de uma linha.

A maioria dos processadores tem seu cache L1 dividido em linhas de 256 bits (32 bytes), isso significa que o processador busca quatro dados de 64 bits (que é o tamanho de seu barramento de dados) utilizando apenas uma única ordem de leitura.

Enquanto tradicionalmente é necessário envio de uma ordem para ler cada dado armazenado no cache de memória, através do modo burst o processador precisa pedir apenas o primeiro dado, para que os demais dados constantes na mesma linha do cache sejam automaticamente lidos.