Pergunta

Isso faz parte da saída do cachegrind.Esta parte do código foi executada 1224 vezes.elmg1 é uma matriz de comprimentos não assinados de tamanho 16 x 20.O tamanho do cache L1 da minha máquina é 32 KB, tamanho da linha de cache 64B e conjunto associativo de 8 vias.

  1. para (eu = 0;eu <20;e++) 78.336 2.448 2 50.184 0 0 1.224 0 0
  2. {
  3. telm01 = elmg1[i];146.880 0 0 73.440 0 0 24.480 0 0
  4. telm31 = (telm01 << 3) ^ val1;97.920 0 0 48.960 0 0 24.480 0 0
  5. telm21 = (telm01 << 2) ^ (val1 >> 1);146.880 1.224 1 48.960 0 0 24.480 0 0
  6. telm11 = (telm01 << 1) ^ (val1 >> 2);146.880 0 0 48.960 0 0 24.480 0 0
  7. }

A.A razão pela qual coloquei isso aqui é que na terceira linha dentro do loop for, vejo uma série de falhas I1 (uma falha L2 também).É um pouco confuso e não consegui adivinhar o motivo?

B.Estou tentando otimizar (tempo) uma parte do código.O texto acima é apenas um pequeno trecho.Acho que no meu programa o acesso à memória está me custando muito.Como no exemplo acima, elmg1 é uma matriz de tamanho 16 x 20 de comprimentos não assinados.Quando tento usar no código, sempre há algumas falhas, e no meu programa essas variáveis ​​ocorrem bastante.Alguma sugestão?

C.Preciso alocar e (às vezes inicializar) esses comprimentos não assinados.Você pode sugerir qual devo preferir, declaração calloc ou array e depois inicialização explícita.A propósito, haverá alguma diferença na forma como o cache os trata?

Obrigado.

Foi útil?

Solução

Você já tentou desenrolar o loop?

  1. Eu não me preocuparia com erros de L1 agora.Além disso, uma falha L2 em 1224 vezes está ok, a CPU precisa carregar os valores no cache em algum momento.
  2. Qual a porcentagem de erros de L2 que esse código custa em comparação com o resto do programa?
  3. Use calloc(), se o tamanho do array for sempre o mesmo e você usar constantes para o tamanho, então o compilador poderá otimizar o zeramento do array.Além disso, a única coisa que afetaria o uso das linhas de cache é o alinhamento, não como ele foi inicializado.

editar:O número foi difícil de ler dessa maneira e leu errado na primeira vez.

vamos ter certeza de que estou lendo os números corretamente na linha 5:

Ir    146,880
I1mr  1,224
ILmr  1
Dr    48,960
D1mr  0
DLmr  0
Dw    24,480
D1mw  0
DLmw  0

O Cache L1 é dividido em dois caches de 32 KB, um para o código I1 e outro para os dados D1.IL e DL são o cache L2 ou L3 que é compartilhado por dados e instruções.

O grande número de I1mr são perdas de instruções e não de dados, isso significa que o código do loop está sendo ejetado do cache de instruções I1.

I1 falha nas linhas 1 e 5 totalizando 3672, que é 3 vezes 1224, então cada vez que o loop é executado, você obtém 3 falhas de cache I1 com linhas de cache de 64 bytes, o que significa que o tamanho do código do loop está entre 128-192 bytes para cobrir 3 linhas de cache.Portanto, essas falhas de I1 na linha 5 são porque é onde o código do loop cruza a última linha do cache.

Eu recomendaria usar o KCachegrind para visualizar os resultados do cachegrind

Editar:Mais sobre linhas de cache.

Esse código de loop não parece estar sendo chamado 1224 vezes sozinho, o que significa que há mais código empurrando esse código para fora do cache I1.

Seu cache I1 de 32 KB é dividido em 512 linhas de cache (64 bytes cada).A parte "conjunto associativo de 8 vias" significa que cada endereço de memória é mapeado para apenas 8 dessas 512 linhas de cache.Se todo o programa que você está perfilando fosse um bloco contínuo de 32 Kbytes de memória, então tudo caberia no cache I1 e nenhum seria ejetado.Provavelmente esse não é o caso e haverá mais de 8 blocos de código de 64 bytes contendo as mesmas 8 linhas de cache.Digamos que todo o seu programa tenha 1Mbyte de código (isso inclui bibliotecas), então cada grupo de 8 linhas de cache terá cerca de 32 (1Mbyte/32Kbyte) pedaços de código contendo essas mesmas 8 linhas de cache.

Leia este artigo do lwn.net para todos os detalhes sangrentos sobre caches de CPU

O compilador nem sempre consegue detectar quais funções do programa serão pontos de acesso (chamados muitas vezes) e quais serão pontos de código (ou seja,código do manipulador de erros, que quase nunca é executado).GCC tem atributos de função quente frio que permitirá marcar funções como quentes/frias, isso permitirá que o compilador agrupe as funções quentes em um bloco de memória para obter melhor uso do cache (ou seja,o código frio não empurrará o código quente para fora dos caches).

De qualquer forma, não vale a pena se preocupar com essas falhas do I1.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top