Quando a CPU com um cache L1 faz uma gravação, o que normalmente acontece é que (assumindo que a linha de cache na qual está gravando já esteja no cache L1) o cache (além de atualizar os dados) marca essa linha de cache como suja , e gravará a linha com os dados atualizados mais tarde.
Uma otimização possível seria fazer com que o cache comparasse o conteúdo da gravação e o conteúdo anterior do cache e, se forem iguais, não marque a linha como suja. Como isso pode permitir que o cache evite retrocessos de vez em quando, posso ver como o fabricante da CPU pode ver isso valendo as portas necessárias para fazer essa lógica.
Minha pergunta: existem CPUs que realizam essa otimização?
Antecedentes do motivo pelo qual estou perguntando: estou escrevendo algum código que precisa ter acesso constante à memória; isto é, alguém que é capaz de ouvir o comportamento do cache não deve deduzir o que estou fazendo. Alguns dos meus acessos são gravações e, da maneira óbvia para implementar esse código, muitas gravações gravam os mesmos dados que já estão lá. Preciso fazer as gravações porque, dependendo dos dados, os dados que estou gravando podem ou não ser os mesmos, e é importante executar a mesma ação independentemente. Se a CPU otimizar realmente não escrevendo uma 'gravação sem alteração', isso significaria que o comportamento do cache variaria dependendo do que estou fazendo, o que subverteria meu objetivo.
Então, existe uma CPU que tenta otimizar as gravações dessa maneira?
Respostas:
Após horas de pesquisa, não consegui encontrar uma CPU que use essa otimização específica. A maioria das otimizações mencionadas geralmente está relacionada a acertos / erros com operações de leitura / gravação e acesso a dados:
(páginas 7 e) https://cseweb.ucsd.edu/classes/fa14/cse240A-a/pdf/08/CSE240A-MBT-L15-Cache.ppt.pdf
No entanto, isso não significa que essa otimização não possa ser executada. Em geral, é possível acessar programaticamente o tamanho de uma linha de cache da CPU. Também é possível acessar os valores atuais nos registros de cache - mas é um pouco perigoso fazê-lo. Se você acessar os registros incorretos em um momento ruim, poderá estar violando os relacionados a um programa em execução. Ou você pode inadvertidamente modificar o conteúdo das linhas que você está tentando ler.
Obtendo o valor atual no cache do registro
Além disso, todas as soluções teóricas requerem alguma forma de implementação de software (assembler). O mais próximo que encontrei se refere à arquitetura ARM, que parece permitir a manipulação de cache. Além disso, você também precisa saber o tamanho de uma linha de cache para a CPU desejada. Você pode ler cuidadosamente o conteúdo do cache para um local secundário na memória, em incrementos do tamanho da linha, e compará-lo com os dados que estão prestes a serem gravados nos registradores (ou linhas de cache L1, neste caso).
Ler o conteúdo do cache da CPU
A partir daí, você pode criar um sistema baseado em software que evite regravações idênticas. Embora isso seja um pouco simplificado, é assim porque a solução precisa ser aplicável a qualquer CPU existente.
Outra possibilidade que encontrei relacionada à coerência do cache:
Trecho relevante de um artigo da Wikipedia sobre coerência de acesso
O ponto principal que chamou minha atenção, em relação a esse problema, foi a descrição do Snarfing:
Em outras palavras, possivelmente existem mecanismos já existentes. Só que eles podem não ser usados para a otimização que você sugeriu. Você precisaria implementar um software que executasse a comparação de leitura / gravação.
fonte
if (mem != x) { mem = x; }
vez demem = x;
. Às vezes, isso é apenas uma otimização para linhas de cache compartilhadas em um programa multithread, porque a gravação interfere na leitura de outros threads.Gravar no cache L1 é uma operação muito, muito crítica.
Escrever exatamente os mesmos dados de volta parece ser bastante raro. Uma otimização que acelera as coisas nesse caso específico não terá muita aceleração no total.
Por outro lado, essa otimização requer uma comparação de dados antigos e novos em cada gravação na memória cache. O que piora isso é que ele exige que os dados a serem gravados estejam realmente disponíveis no momento da gravação!
Isso geralmente não é o caso em uma CPU moderna. Os dados a serem gravados ainda podem estar sendo calculados, por exemplo. O cache ainda pode prosseguir, carregar a linha de cache, se necessário, marcar a linha de cache como modificada e assim por diante, mesmo antes de o cálculo ser concluído. Toda a contabilidade já pode ser executada, exceto a modificação real da linha de cache. Se você deseja comparar resultados recém-gravados e dados antigos da linha de cache, isso não é possível.
Como exemplo, se você tiver o código C a [i] = x / y; a divisão x / y leva um tempo extraordinário para ser executada na maioria das CPUs. No entanto, a maior parte do trabalho necessário para armazenar o resultado em um [i] aconteceu muito antes da divisão terminar; a única coisa que falta é a movimentação de oito bytes de resultado para a linha de cache. Uma operação que libera a linha de cache espera automaticamente até que a divisão seja concluída. Uma operação que lê um [i] provavelmente será redirecionada para obter o resultado direto do divisor.
fonte
Essa otimização não dobrará o tempo que a CPU precisa para gravar algo no cache? Como cada gravação de linha de cache agora será acompanhada de uma operação de comparação, que não é gratuita.
Portanto, atualmente a otimização dependerá do fator muito vago: quantas vezes um software médio reescreve sua memória armazenável em cache com os mesmos dados.
fonte