Quanto espaço de página é usado para o processador. O que é memória cache do disco rígido e para que serve? Arquiteturas e princípios de cache

Quanto espaço de página é usado para o processador. O que é memória cache do disco rígido e para que serve? Arquiteturas e princípios de cache

Memória cache (esconderijo, dinheiro, amortecedor- eng.) - usado em dispositivos digitais como área de transferência de alta velocidade. A memória cache pode ser encontrada em dispositivos de computador, como processadores, placas de rede, unidades de CD e muitos outros.

O princípio operacional e a arquitetura do cache podem variar bastante.

Por exemplo, um cache pode servir como um prancheta . O dispositivo processa os dados e os transfere para um buffer de alta velocidade, onde o controlador transmite os dados para a interface. Esse cache tem como objetivo evitar erros, verificar a integridade dos dados de hardware ou codificar um sinal de um dispositivo em um sinal compreensível para a interface, sem atrasos. Este sistema é utilizado, por exemplo, em CD/DVD Unidades de CD.

Em outro caso, o cache pode servir para armazenando código usado com frequência e, assim, acelerar o processamento de dados. Ou seja, o dispositivo não precisa calcular ou consultar novamente os dados, o que levaria muito mais tempo do que lê-los no cache. Neste caso, o tamanho e a velocidade do cache desempenham um papel muito importante.

Essa arquitetura é mais frequentemente encontrada em discos rígidos e unidades centrais de processamento ( CPU).

Quando os dispositivos estão operando, firmware especial ou programas despachantes podem ser carregados no cache, o que funcionaria mais lentamente com ROM(memória somente leitura).

A maioria dos dispositivos modernos usa tipo de cache misto , que pode servir como área de transferência e também armazenar códigos usados ​​com frequência.

Existem diversas funções muito importantes implementadas para o cache dos processadores e chips de vídeo.

Mesclando unidades de execução . Unidades centrais de processamento e processadores de vídeo geralmente usam um cache compartilhado rápido entre núcleos. Assim, se um núcleo processou informações e está no cache, e um comando é recebido para a mesma operação, ou para trabalhar com esses dados, então os dados não serão processados ​​​​pelo processador novamente, mas serão retirados do cache para processamento posterior. O kernel será descarregado para processar outros dados. Isso aumenta significativamente o desempenho em cálculos semelhantes, mas complexos, especialmente se o cache for grande e rápido.

Cache compartilhado, também permite que os kernels trabalhem diretamente com ele, ignorando o lento .

Cache para instruções. Existe um cache L1 compartilhado e muito rápido para instruções e outras operações, ou um cache dedicado para elas. Quanto mais instruções armazenadas em um processador, maior será o cache de instruções necessário. Isso reduz a latência da memória e permite que o bloco de instruções funcione de forma quase independente.Quando está cheio, o bloco de instruções começa a ficar ocioso periodicamente, o que diminui a velocidade do cálculo.

Outras funções e recursos.

Vale ressaltar que em CPU(unidades centrais de processamento), aplicadas correção de erros de hardware (ECC), porque um pequeno erro no cache pode levar a um erro contínuo durante o processamento adicional desses dados.

EM CPU E GPU existe hierarquia de cache , que permite separar dados de núcleos individuais e gerais. Embora quase todos os dados do cache de segundo nível ainda sejam copiados para o terceiro nível geral, mas nem sempre. O primeiro nível de cache é o mais rápido e cada nível subsequente é mais lento, mas maior em tamanho.

Para processadores, é considerado normal três e menos níveis de cache. Isso permite um equilíbrio entre velocidade, tamanho do cache e dissipação de calor. É difícil encontrar mais de dois níveis de cache em processadores de vídeo.

Tamanho do cache, impacto no desempenho e outras características.

Naturalmente, quanto maior o cache, mais dados ele pode armazenar e processar, mas há um problema sério.

Grande cache- Esse grande orçamento. Em processadores de servidor ( CPU), o cache pode usar até 80% orçamento de transistor. Em primeiro lugar, isto afecta o custo final e, em segundo lugar, o consumo de energia e a dissipação de calor aumentam, o que não é comparável ao aumento da produtividade em vários por cento.

Um dos fatores importantes que aumentam o desempenho do processador é a presença de memória cache, ou melhor, seu volume, velocidade de acesso e distribuição entre níveis.

Há já algum tempo que quase todos os processadores estão equipados com este tipo de memória, o que mais uma vez comprova a utilidade da sua presença. Neste artigo falaremos sobre a estrutura, níveis e finalidade prática da memória cache, o que é muito importante. características do processador.

O que é memória cache e sua estrutura

A memória cache é uma memória ultrarrápida usada pelo processador para armazenar temporariamente os dados acessados ​​com mais frequência. É assim que podemos descrever brevemente esse tipo de memória.

A memória cache é construída em flip-flops, que, por sua vez, consistem em transistores. Um grupo de transistores ocupa muito mais espaço do que os mesmos capacitores que compõem o BATER. Isto acarreta muitas dificuldades na produção, bem como limitações de volume. É por isso que a memória cache é uma memória muito cara, embora tenha volumes insignificantes. Mas dessa estrutura vem a principal vantagem dessa memória - a velocidade. Como os flip-flops não precisam de regeneração e o tempo de atraso da porta na qual estão montados é pequeno, o tempo para mudar o flip-flop de um estado para outro ocorre muito rapidamente. Isso permite que a memória cache opere nas mesmas frequências dos processadores modernos.

Além disso, um fator importante é o posicionamento da memória cache. Ele está localizado no próprio chip do processador, o que reduz significativamente o tempo de acesso. Anteriormente, a memória cache de alguns níveis estava localizada fora do chip do processador, em um chip SRAM especial em algum lugar da placa-mãe. Agora, quase todos os processadores possuem memória cache localizada no chip do processador.


Para que é usado o cache do processador?

Conforme mencionado acima, o principal objetivo da memória cache é armazenar dados que são frequentemente usados ​​pelo processador. O cache é um buffer no qual os dados são carregados e, apesar de seu tamanho pequeno (cerca de 4 a 16 MB), processadores modernos, proporciona um aumento significativo de desempenho em qualquer aplicativo.

Para entender melhor a necessidade de memória cache, vamos imaginar organizar a memória de um computador como se fosse um escritório. A RAM será um gabinete com pastas que o contador acessa periodicamente para recuperar grandes blocos de dados (ou seja, pastas). E a mesa será uma memória cache.

Há elementos que são colocados na mesa do contador, aos quais ele se refere diversas vezes ao longo de uma hora. Por exemplo, podem ser números de telefone, alguns exemplos de documentos. Esses tipos de informações ficam localizados diretamente na mesa, o que, por sua vez, aumenta a velocidade de acesso a elas.

Da mesma forma, os dados podem ser adicionados desses grandes blocos de dados (pastas) à tabela para uso rápido, por exemplo, um documento. Quando este documento não é mais necessário, ele é colocado de volta no gabinete (na RAM), limpando assim a tabela (memória cache) e liberando esta tabela para novos documentos que serão utilizados no próximo período de tempo.

Também com a memória cache, se houver algum dado com maior probabilidade de ser acessado novamente, esses dados da RAM serão carregados na memória cache. Muitas vezes, isso acontece através do co-carregamento dos dados que têm maior probabilidade de serem usados ​​após os dados atuais. Ou seja, existem suposições sobre o que será utilizado “depois”. Estes são os princípios operacionais complexos.

Níveis de cache do processador

Os processadores modernos estão equipados com um cache, que geralmente consiste em 2 ou 3 níveis. Claro que existem exceções, mas muitas vezes é esse o caso.

Em geral, podem existir os seguintes níveis: L1 (primeiro nível), L2 (segundo nível), L3 (terceiro nível). Agora um pouco mais de detalhes sobre cada um deles:

Cache de primeiro nível (L1)– o nível de memória cache mais rápido que trabalha diretamente com o núcleo do processador, graças a essa interação estreita, esse nível tem o menor tempo de acesso e opera em frequências próximas ao processador. É um buffer entre o processador e o cache de segundo nível.

Consideraremos os volumes em um processador Intel Core i7-3770K de alto desempenho. Este processador está equipado com cache L1 de 4x32 KB 4 x 32 KB = 128 KB. (32 KB por núcleo)

Cache de segundo nível (L2)– o segundo nível é de maior escala que o primeiro, mas como resultado, tem “características de velocidade” mais baixas. Conseqüentemente, serve como um buffer entre os níveis L1 e L3. Se olharmos novamente para nosso exemplo Core i7-3770 K, o tamanho da memória cache L2 é 4x256 KB = 1 MB.

Cache de nível 3 (L3)– o terceiro nível, novamente, é mais lento que os dois anteriores. Mas ainda é muito mais rápido que a RAM. O tamanho do cache L3 no i7-3770K é de 8 MB. Se os dois níveis anteriores forem compartilhados por cada núcleo, esse nível será comum a todo o processador. O indicador é bastante sólido, mas não exorbitante. Já que, por exemplo, para processadores da série Extreme como o i7-3960X, são 15 MB, e para alguns novos processadores Xeon, mais de 20.

O que é cache do processador?

Um cache é uma parte da memória que fornece velocidade máxima de acesso e acelera a velocidade de computação. Ele armazena os dados que o processador solicita com mais frequência, para que o processador não precise acessar constantemente a memória do sistema para obtê-los.

Como você sabe, esta é uma parte do equipamento de informática que se caracteriza pelas velocidades mais lentas de troca de dados. Se o processador precisar de alguma informação, ela vai para a RAM através do barramento de mesmo nome. Ao receber uma solicitação do processador, ele começa a se aprofundar em seus anais em busca dos dados de que o processador necessita. Após o recebimento, a RAM os envia de volta ao processador pelo mesmo barramento de memória. Este círculo para troca de dados sempre foi muito longo. Portanto, os fabricantes decidiram que poderiam permitir que o processador armazenasse dados em algum lugar próximo. O funcionamento da cache baseia-se numa ideia simples.

Pense na memória como uma biblioteca escolar. O aluno aproxima-se da funcionária para pegar um livro, ela vai até as estantes, procura, devolve ao aluno, prepara-o adequadamente e passa para o próximo aluno. No final do dia, ele repete a mesma operação quando os livros lhe são devolvidos. É assim que funciona um processador sem cache.

Por que o processador precisa de um cache?

Agora imagine que a bibliotecária está cansada de correr constantemente para frente e para trás com livros que são constantemente exigidos dela, ano após ano, dia após dia. Adquiriu um grande armário onde guarda os livros e manuais mais solicitados. O resto que foi colocado, claro, continua guardado nas mesmas prateleiras. Mas estes estão sempre à mão. Quanto tempo ele economizou com esse gabinete, tanto para ele quanto para os demais. Esta é a cache.

Então, o cache só pode armazenar os dados mais necessários?

Sim. Mas ele pode fazer mais. Por exemplo, tendo já armazenado dados frequentemente solicitados, é capaz de avaliar (com a ajuda do processador) a situação e solicitar informações que estão prestes a ser necessárias. Então, um cliente locador de vídeo que solicitou o filme “Die Hard” com a primeira parte provavelmente pedirá a segunda. E aqui está ela! O mesmo vale para o cache do processador. Ao acessar a RAM e armazenar determinados dados, ele também recupera dados de células de memória vizinhas. Esses dados são chamados de linhas de cache.

O que é um cache de dois níveis?

Um processador moderno possui dois níveis. Assim, o primeiro e o segundo. Eles são designados pela letra L do Nível de Inglês. O primeiro - L1 - é mais rápido, mas tem volume pequeno. O segundo - L2 - é um pouco maior, mas mais lento, porém mais rápido que a RAM. O cache de primeiro nível é dividido em cache de instruções e cache de dados. O cache de instruções armazena o conjunto de instruções que o processador necessita para cálculos. Já o cache de dados armazena quantidades ou valores necessários para o cálculo atual. E o cache de segundo nível é usado para carregar dados da RAM do computador. O princípio de funcionamento dos níveis de cache também pode ser explicado usando o exemplo de uma biblioteca escolar. Assim, depois de encher o armário comprado, o bibliotecário percebe que não há mais livros suficientes, para os quais tem que correr constantemente pelo corredor. Mas a lista desses livros foi finalizada e você precisa comprar o mesmo armário. Ele não jogou fora o primeiro - é uma pena - e simplesmente comprou o segundo. E agora, à medida que o primeiro é preenchido, o bibliotecário começa a preencher o segundo, que entra em ação quando o primeiro está cheio, mas os livros necessários não cabem nele. O mesmo acontece com os níveis de cache. E à medida que a tecnologia do microprocessador se desenvolve, os níveis de cache do processador aumentam de tamanho.

O cache continuará a crescer?

Dificilmente. A busca pela frequência do processador também não durou muito e os fabricantes encontraram outras maneiras de aumentar a potência. O mesmo acontece com o cache. Especificamente falando, o volume e o número de níveis não podem ser aumentados indefinidamente. O cache não deve se transformar em outro stick de RAM com velocidade de acesso lenta ou reduzir o tamanho do processador à metade do tamanho da placa-mãe. Afinal, a velocidade de acesso aos dados é, antes de tudo, o consumo de energia e o custo de desempenho do próprio processador. As falhas de cache (em oposição aos acertos de cache), onde o processador acessa a memória em cache para dados que não estão lá, também se tornaram mais frequentes. Os dados no cache são constantemente atualizados usando vários algoritmos para aumentar a probabilidade de um acerto no cache.

Leia: 644

Quase todos os desenvolvedores sabem que o cache do processador é uma memória pequena, mas rápida, que armazena dados de áreas de memória visitadas recentemente - a definição é curta e bastante precisa. No entanto, conhecer os detalhes enfadonhos sobre os mecanismos de cache é necessário para compreender os fatores que afetam o desempenho do código.

Neste artigo veremos vários exemplos que ilustram vários recursos de caches e seu impacto no desempenho. Os exemplos serão em C#; a escolha da linguagem e plataforma não afeta muito a avaliação de desempenho e as conclusões finais. Naturalmente, dentro de limites razoáveis, se você escolher uma linguagem na qual ler um valor de um array seja equivalente a acessar uma tabela hash, você não obterá nenhum resultado interpretável. As notas do tradutor estão em itálico.

Habracut - - -

Exemplo 1: Acesso e desempenho à memória

Quanto mais rápido você acha que o segundo ciclo é do que o primeiro?
int arr = novo int;

// primeiro
para (int eu = 0; eu< arr.Length; i++) arr[i] *= 3;

// segundo
para (int eu = 0; eu< arr.Length; i += 16) arr[i] *= 3;


O primeiro loop multiplica todos os valores da matriz por 3, o segundo loop multiplica apenas cada décimo sexto valor. O segundo ciclo só termina 6% trabalho o primeiro ciclo, mas nas máquinas modernas ambos os ciclos são executados em tempo aproximadamente igual: 80ms E 78ms respectivamente (na minha máquina).

A solução é simples: acesso à memória. A velocidade desses loops é determinada principalmente pela velocidade do subsistema de memória, e não pela velocidade da multiplicação de inteiros. Como veremos no próximo exemplo, o número de acessos à RAM é o mesmo tanto no primeiro quanto no segundo caso.

Exemplo 2: Impacto das Linhas de Cache

Vamos nos aprofundar e tentar outros valores de etapa, não apenas 1 e 16:
para (int eu = 0; eu< arr.Length; i += K /* шаг */ ) arr[i] *= 3;

Aqui estão os tempos de execução deste loop para diferentes valores de etapa K:

Observe que com valores de passo de 1 a 16, o tempo de operação permanece praticamente inalterado. Mas com valores maiores que 16, o tempo de execução diminui cerca de metade cada vez que dobramos o passo. Isso não significa que o loop, de alguma forma, magicamente comece a rodar mais rápido, apenas que o número de iterações também diminui. O ponto chave é o mesmo tempo de operação com valores de passo de 1 a 16.

A razão para isso é que os processadores modernos não acessam a memória um byte por vez, mas sim em pequenos blocos chamados linhas de cache. Normalmente, o tamanho da string é de 64 bytes. Quando você lê qualquer valor da memória, pelo menos uma linha de cache entra no cache. O acesso subsequente a qualquer valor desta linha é muito rápido.

Como 16 valores int ocupam 64 bytes, os loops com etapas de 1 a 16 acessam o mesmo número de linhas de cache, ou mais precisamente, todas as linhas de cache do array. No passo 32, o acesso ocorre a cada segunda linha, no passo 64, a cada quarta.

Compreender isso é muito importante para algumas técnicas de otimização. O número de acessos a ele depende da localização dos dados na memória. Por exemplo, dados não alinhados podem exigir dois acessos à memória principal em vez de um. Como descobrimos acima, a velocidade operacional será duas vezes menor.

Exemplo 3: tamanhos de cache de nível 1 e 2 (L1 e L2)

Os processadores modernos normalmente possuem dois ou três níveis de cache, geralmente chamados de L1, L2 e L3. Para descobrir os tamanhos dos caches em diferentes níveis, você pode usar o utilitário CoreInfo ou a função GetLogicalProcessorInfo da API do Windows. Ambos os métodos também fornecem informações sobre o tamanho da linha de cache para cada nível.

Na minha máquina, o CoreInfo relata caches de dados L1 de 32 KB, caches de instruções L1 de 32 KB e caches de dados L2 de 4 MB. Cada núcleo tem seus próprios caches L1 pessoais, os caches L2 são compartilhados por cada par de núcleos:

Processador lógico para mapa de cache: *--- Cache de dados 0, Nível 1, 32 KB, Assoc 8, LineSize 64 *--- Cache de instruções 0, Nível 1, 32 KB, Assoc 8, LineSize 64 -*-- Cache de dados 1, Nível 1, 32 KB, Assoc 8, LineSize 64 -*-- Cache de Instrução 1, Nível 1, 32 KB, Assoc 8, LineSize 64 **-- Cache Unificado 0, Nível 2, 4 MB, Assoc 16, LineSize 64 --*- Cache de dados 2, Nível 1, 32 KB, Assoc 8, LineSize 64 --*- Cache de instruções 2, Nível 1, 32 KB, Assoc 8, LineSize 64 ---* Cache de dados 3, Nível 1, 32 KB, Assoc 8, LineSize 64 ---* Cache de instrução 3, Nível 1, 32 KB, Assoc 8, LineSize 64 --** Cache unificado 1, Nível 2, 4 MB, Assoc 16, LineSize 64
Vamos verificar essa informação experimentalmente. Para fazer isso, vamos percorrer nosso array, incrementando a cada 16 valores - uma maneira fácil de alterar os dados em cada linha do cache. Quando chegamos ao fim, voltamos ao início. Vamos verificar diferentes tamanhos de array; devemos ver uma queda no desempenho quando o array não cabe mais em caches de diferentes níveis.

O código é:

passos internos = 64 * 1024 * 1024; //número de iterações
int comprimentoMod = arr.Length - 1; //tamanho do array -- potência de dois

para (int eu = 0; eu< steps; i++)
{
// x & lengthMod = x% arr.Length, porque potências de dois
arr[(i * 16) & lengthMod]++;
}


Resultado dos testes:

Na minha máquina, há quedas perceptíveis no desempenho após 32 KB e 4 MB - esses são os tamanhos dos caches L1 e L2.

Exemplo 4: Paralelismo de Instrução

Agora vamos ver outra coisa. Na sua opinião, qual desses dois loops será executado mais rapidamente?
passos internos = 256 * 1024 * 1024;
int a = novo int;

// primeiro
para (int eu = 0; eu< steps; i++) { a++; a++; }

// segundo
para (int eu = 0; eu< steps; i++) { a++; a++; }


Acontece que o segundo loop funciona quase duas vezes mais rápido, pelo menos em todas as máquinas que testei. Por que? Porque os comandos dentro dos loops têm diferentes dependências de dados. Os primeiros comandos possuem a seguinte cadeia de dependências:

No segundo ciclo as dependências são:

As partes funcionais dos processadores modernos são capazes de executar um certo número de operações simultaneamente, geralmente não um número muito grande. Por exemplo, é possível o acesso paralelo aos dados do cache L1 em ​​dois endereços, e também é possível a execução simultânea de duas instruções aritméticas simples. No primeiro ciclo, o processador não pode usar esses recursos, mas pode no segundo.

Exemplo 5: Associatividade de Cache

Uma das principais questões que devem ser respondidas ao projetar um cache é se os dados de uma determinada região da memória podem ser armazenados em quaisquer células de cache ou apenas em algumas delas. Três soluções possíveis:
  1. Cache de mapeamento direto Os dados de cada linha de cache na RAM são armazenados em apenas um local de cache predefinido. A maneira mais simples de calcular o mapeamento é: row_index_in_memory % number_of_cache_cells. Duas linhas mapeadas para a mesma célula não podem estar no cache ao mesmo tempo.
  2. Cache associativo parcial de N entradas, cada linha pode ser armazenada em N locais de cache diferentes. Por exemplo, em um cache de 16 entradas, uma linha pode ser armazenada em uma das 16 células que compõem o grupo. Normalmente, linhas com bits de índices menos significativos iguais compartilham um grupo.
  3. Cache totalmente associativo, qualquer linha pode ser armazenada em qualquer local de cache. A solução é equivalente a uma tabela hash em seu comportamento.
Caches mapeados diretamente são propensos a contenção, por exemplo, quando duas linhas competem pela mesma célula, alternadamente expulsando uma da outra do cache, a eficiência é muito baixa. Por outro lado, as caches totalmente associativas, embora livres desta desvantagem, são muito complexas e caras de implementar. Caches parcialmente associativos são uma compensação típica entre complexidade e eficiência de implementação.

Por exemplo, na minha máquina, o cache L2 de 4 MB é um cache associativo parcial de 16 entradas. Toda a RAM é dividida em conjuntos de linhas de acordo com os bits menos significativos de seus índices, as linhas de cada conjunto competem por um grupo de 16 células de cache L2.

Como o cache L2 possui 65.536 células (4 * 2 20/64) e cada grupo consiste em 16 células, temos um total de 4.096 grupos. Assim, os 12 bits inferiores do índice de linha determinam a qual grupo esta linha pertence (2 12 = 4.096). Como resultado, linhas com endereços múltiplos de 262.144 (4.096 * 64) compartilham o mesmo grupo de 16 células e competem por espaço nele.

Para que os efeitos da associatividade tenham efeito, precisamos acessar constantemente um grande número de linhas de um mesmo grupo, por exemplo, utilizando o seguinte código:

público estático longo UpdateEveryKthByte (byte arr, int K)
{
const int representante = 1024 * 1024; //número de iterações

Cronômetro sw = Stopwatch.StartNew();

int p = 0;
para (int eu = 0; eu< rep; i++)
{
arr[p]++;

P+=K; se (p >= arr.Comprimento) p = 0;
}

Sw.Stop();
retornar sw.ElapsedMilliseconds;
}


O método incrementa cada K-ésimo elemento do array. Quando chegarmos ao fim, começaremos de novo. Após um grande número de iterações (2 20), paramos. Fiz execuções para diferentes tamanhos de array e valores de etapas K. Resultados (azul - tempo de execução longo, branco - curto):

As áreas azuis correspondem aos casos em que, com constantes alterações de dados, o cache não é capaz de acomodar todos os dados necessários de uma só vez. A cor azul brilhante indica um tempo de operação de cerca de 80 ms, quase branco - 10 ms.

Vamos lidar com as áreas azuis:

  1. Por que aparecem linhas verticais? As linhas verticais correspondem aos valores das etapas nas quais muitas linhas (mais de 16) de um grupo são acessadas. Para esses valores, o cache de 16 entradas da minha máquina não pode acomodar todos os dados necessários.

    Alguns dos valores de passada ruins são potências de dois: 256 e 512. Por exemplo, considere a passada 512 e uma matriz de 8 MB. Com esta etapa, existem 32 seções na matriz (8 * 2 20/262 144), que competem entre si por células em 512 grupos de cache (262 144/512). Existem 32 seções, mas existem apenas 16 células no cache para cada grupo, portanto não há espaço suficiente para todos.

    Outros valores de etapas que não são potências de dois são simplesmente azarados, o que causa um grande número de acessos aos mesmos grupos de cache e também leva ao aparecimento de linhas verticais azuis na figura. Neste ponto, os amantes da teoria dos números são convidados a pensar.

  2. Por que as linhas verticais quebram no limite de 4 MB? Quando o tamanho do array é 4 MB ou menos, o cache de 16 entradas se comporta como um cache totalmente associativo, ou seja, pode acomodar todos os dados do array sem conflitos. Não há mais de 16 áreas lutando por um grupo de cache (262.144 * 16 = 4 * 2 20 = 4 MB).
  3. Por que há um grande triângulo azul no canto superior esquerdo? Porque com um pequeno passo e um array grande, o cache não consegue acomodar todos os dados necessários. O grau de associatividade do cache desempenha um papel secundário aqui; a limitação está relacionada ao tamanho do cache L2.

    Por exemplo, com um tamanho de array de 16 MB e um avanço de 128, acessamos cada 128 bytes, modificando assim cada segunda linha de cache do array. Para armazenar cada segunda linha no cache, você precisa de 8 MB de cache, mas na minha máquina só tenho 4 MB.

    Mesmo que o cache fosse totalmente associativo, não permitiria armazenar 8 MB de dados nele. Observe que no exemplo já discutido com um espaçamento de 512 e um tamanho de array de 8 MB, precisamos apenas de 1 MB de cache para armazenar todos os dados necessários, mas isso é impossível devido à associatividade insuficiente do cache.

  4. Por que o lado esquerdo do triângulo ganha gradualmente em intensidade? A intensidade máxima ocorre em um valor de passo de 64 bytes, que é igual ao tamanho da linha de cache. Como vimos no primeiro e no segundo exemplos, o acesso sequencial à mesma linha não custa quase nada. Digamos que com um passo de 16 bytes temos quatro acessos à memória pelo preço de um.

    Como o número de iterações é o mesmo em nosso teste para qualquer valor de etapa, uma etapa mais barata resulta em menos tempo de execução.

Os efeitos descobertos persistem em valores de parâmetros grandes:

A associatividade do cache é algo interessante que pode se manifestar sob certas condições. Ao contrário dos outros problemas discutidos neste artigo, não é tão grave. Definitivamente não é algo que requer atenção constante ao escrever programas.

Exemplo 6: particionamento de cache falso

Em máquinas com vários núcleos, você pode encontrar outro problema: coerência do cache. Os núcleos do processador possuem caches parcial ou totalmente separados. Na minha máquina, os caches L1 são separados (como sempre) e também há dois caches L2 compartilhados por cada par de núcleos. Os detalhes podem variar, mas em geral, os processadores multi-core modernos possuem caches hierárquicos de vários níveis. Além disso, os caches mais rápidos, mas também os menores, pertencem a núcleos individuais.

Quando um núcleo modifica um valor em seu cache, outros núcleos não podem mais usar o valor antigo. O valor nos caches de outros núcleos deve ser atualizado. Além disso, deve ser atualizado toda a linha de cache, já que os caches operam em dados no nível da linha.

Vamos demonstrar esse problema com o seguinte código:

private static int s_counter = new int;

private void UpdateCounter (posição int)
{
para (int j = 0; j< 100000000; j++)
{
contador_s = contador_s + 3;
}
}


Se na minha máquina de quatro núcleos eu chamar esse método com os parâmetros 0, 1, 2, 3 simultaneamente de quatro threads, o tempo de execução será 4,3 segundos. Mas se eu chamar o método com parâmetros 16, 32, 48, 64, então o tempo de execução será apenas 0,28 segundos.

Por que? No primeiro caso, todos os quatro valores processados ​​pelos threads em um determinado momento provavelmente terminarão em uma linha de cache. Cada vez que um núcleo incrementa um valor, ele marca as células de cache que contêm esse valor em outros núcleos como inválidas. Após esta operação, todos os outros kernels terão que armazenar a linha em cache novamente. Isso torna o mecanismo de cache inoperante, prejudicando o desempenho.

Exemplo 7: Complexidade de Hardware

Mesmo agora, quando os princípios de operação do cache não são segredo para você, o hardware ainda lhe trará surpresas. Os processadores diferem uns dos outros nos métodos de otimização, heurísticas e outras sutilezas de implementação.

O cache L1 de alguns processadores pode acessar duas células em paralelo se pertencerem a grupos diferentes, mas se pertencerem ao mesmo grupo, apenas sequencialmente. Pelo que eu sei, alguns podem até acessar diferentes partes da mesma célula em paralelo.

Os processadores podem surpreendê-lo com otimizações inteligentes. Por exemplo, o código do exemplo anterior sobre falso compartilhamento de cache não funciona como esperado no meu computador doméstico - nos casos mais simples, o processador pode otimizar o trabalho e reduzir os efeitos negativos. Se você modificar um pouco o código, tudo se encaixará.

Aqui está outro exemplo de peculiaridades estranhas de hardware:

privado estático int A, B, C, D, E, F, G;

vazio estático privado Estranheza()
{
para (int eu = 0; eu< 200000000; i++)
{
<какой-то код>
}
}


Se em vez disso<какой-то код>Substitua três opções diferentes e você poderá obter os seguintes resultados:

Aumentar os campos A, B, C, D leva mais tempo do que incrementar os campos A, C, E, G. O que é ainda mais estranho é que incrementar os campos A e C leva mais tempo do que os campos A, C E E, G. Não sei exatamente quais são as razões para isso, mas talvez estejam relacionadas aos bancos de memória ( sim, sim, com bancos de memória comuns de três litros, e não o que você pensava). Se você tiver alguma opinião sobre este assunto, fale nos comentários.

Na minha máquina, o acima não é observado, no entanto, às vezes há resultados anormalmente ruins - provavelmente, o agendador de tarefas faz seus próprios “ajustes”.

A lição a ser aprendida com este exemplo é que é muito difícil prever completamente o comportamento do hardware. Sim, Pode prever muito, mas você precisa confirmar constantemente suas previsões por meio de medições e testes.

Conclusão

Espero que tudo discutido acima tenha ajudado você a entender a estrutura dos caches do processador. Agora você pode colocar esse conhecimento em prática para otimizar seu código.

Cache é uma memória embutida no processador na qual são gravados os dados (comandos) da RAM usados ​​​​com mais frequência, o que acelera significativamente a operação.

Tamanho do cache L1 (de 8 a 128 KB)
Tamanho do cache de nível 1.
O cache de nível 1 é um bloco de memória de alta velocidade localizado diretamente no núcleo do processador.
Os dados extraídos da RAM são copiados para ela.

O armazenamento de instruções principais melhora o desempenho do processador devido à velocidade de processamento de dados mais rápida (o processamento do cache é mais rápido do que da RAM).

A capacidade do cache de primeiro nível é pequena e chega a quilobytes.
Normalmente, os modelos de processador “antigos” possuem um cache L1 maior.
Para modelos multi-core, é indicada a quantidade de memória cache L1 para um núcleo.

Tamanho do cache L2 (de 128 a 12.288 KB)
Tamanho do cache de nível 2.
O cache L2 é um bloco de memória de alta velocidade que executa as mesmas funções do cache L1 (consulte "Capacidade do cache L1"), mas possui velocidade menor e maior capacidade.

Se você estiver escolhendo um processador para tarefas que consomem muitos recursos, será preferível um modelo com grande cache L2.
Para processadores multi-core, a quantidade total de memória cache de segundo nível é indicada.

Tamanho do cache L3 (de 0 a 16.384 KB)
Tamanho do cache de nível 3.
O cache L3 integrado, combinado com um barramento de sistema rápido, forma um canal de troca de dados de alta velocidade com a memória do sistema.

Via de regra, apenas CPUs para soluções de servidor ou edições especiais de processadores “desktop” são equipadas com memória cache de terceiro nível.

Por exemplo, linhas de processadores como Intel Pentium 4 Extreme Edition, Xeon DP, Itanium 2, Xeon MP e outras possuem memória cache de terceiro nível.

Twin BiCS FLASH - nova tecnologia de memória flash 3D

Em 11 de dezembro de 2019, no IEEE International Electronic Devices Meeting (IEDM), a TOKYO-Kioxia Corporation anunciou a tecnologia de memória flash 3D - Twin BiCS FLASH.

Driver AMD Radeon Software Adrenalin Edition 2020 19.12.2 WHQL (adicionado)

Em 10 de dezembro, a AMD apresentou o mega driver Radeon Software Adrenalin 2020 Edition 19.12.2 WHQL.

Atualização cumulativa do Windows 10 1909 KB4530684

Em 10 de dezembro de 2019, a Microsoft lançou a atualização cumulativa KB4530684 (Build 18363.535) para a atualização de novembro de 2019 do Windows 10 (versão 1909) em processadores x86, x64 (amd64), ARM64 e Windows Server 2019 (1909) para sistemas baseados em x64.

Driver NVIDIA Game Ready GeForce 441.66 WHQL

O driver NVIDIA GeForce Game Ready 441.66 WHQL inclui suporte para MechWarrior 5: Mercenaries e Detroit: Become Human, e também adiciona suporte G-SYNC para monitores MSI MAG251RX e ViewSonic XG270.

Visualizações