Objetivo do alinhamento da memória

195

É certo que eu não entendo. Digamos que você tenha uma memória com uma palavra de duração de 1 byte. Por que você não pode acessar uma variável de 4 bytes de comprimento em um único acesso à memória em um endereço não alinhado (ou seja, não divisível por 4), como é o caso dos endereços alinhados?

arca
fonte
17
Depois de fazer algumas adicional Googling eu encontrei este grande link, que explica o problema muito bem.
Ark
Confira este pequeno artigo para as pessoas que começam a aprender esta: blog.virtualmethodstudio.com/2017/03/memory-alignment-run-fools
darkgaze
3
@ark link broken
John Jiang
2
@JohnJiang Acho que encontrei o novo link aqui: developer.ibm.com/technologies/systems/articles/pa-dalign
ejohnso49

Respostas:

62

É uma limitação de muitos processadores subjacentes. Geralmente, isso pode ser resolvido com quatro buscas ineficientes de um byte, em vez de uma busca eficiente de palavras, mas muitos especificadores de idioma decidiram que seria mais fácil apenas proibi-los e forçar o alinhamento de tudo.

Há muito mais informações neste link que o OP descobriu.

Paul Tomblin
fonte
310

O subsistema de memória em um processador moderno é restrito ao acesso à memória na granularidade e alinhamento do tamanho da palavra; esse é o caso por várias razões.

Rapidez

Os processadores modernos têm vários níveis de memória cache nos quais os dados devem ser extraídos; o suporte a leituras de byte único faria com que a taxa de transferência do subsistema de memória estivesse fortemente ligada à taxa de transferência da unidade de execução (também conhecida como limite da CPU); tudo isso lembra como o modo PIO foi superado pelo DMA por muitos dos mesmos motivos nos discos rígidos.

A CPU sempre lê em seu tamanho de palavra (4 bytes em um processador de 32 bits); portanto, quando você acessa um endereço desalinhado - em um processador que a suporta -, o processador lê várias palavras. A CPU lerá cada palavra de memória que o endereço solicitado se encontra. Isso causa uma amplificação de até 2X o número de transações de memória necessárias para acessar os dados solicitados.

Por esse motivo, pode ser muito mais lento ler dois bytes que quatro. Por exemplo, digamos que você tenha uma estrutura na memória parecida com esta:

struct mystruct {
    char c;  // one byte
    int i;   // four bytes
    short s; // two bytes
}

Em um processador de 32 bits, provavelmente seria alinhado como mostrado aqui:

Layout da estrutura

O processador pode ler cada um desses membros em uma transação.

Digamos que você tenha uma versão compactada da estrutura, talvez da rede em que foi compactada para obter eficiência de transmissão; pode ser algo como isto:

Estrutura embalada

Ler o primeiro byte será o mesmo.

Quando você solicita que o processador forneça 16 bits de 0x0005, ele precisará ler uma palavra de 0x0004 e deslocar para a esquerda 1 byte para colocá-lo em um registro de 16 bits; algum trabalho extra, mas a maioria pode lidar com isso em um ciclo.

Quando você pede 32 bits de 0x0001, obtém uma amplificação de 2X. O processador lerá de 0x0000 no registro de resultados e mudará 1 byte para a esquerda, depois lerá novamente de 0x0004 para um registro temporário, mudará para a direita 3 bytes e depois ORcom o registro de resultados.

Alcance

Para qualquer espaço de endereço especificado, se a arquitetura puder assumir que os 2 LSBs são sempre 0 (por exemplo, máquinas de 32 bits), poderá acessar 4 vezes mais memória (os 2 bits salvos podem representar 4 estados distintos) ou a mesma quantidade de memória com 2 bits para algo como bandeiras. Desativar os 2 LSBs de um endereço daria um alinhamento de 4 bytes; também conhecido como passo de 4 bytes. Cada vez que um endereço é incrementado, ele efetivamente incrementa o bit 2, e não o 0, ou seja, os últimos 2 bits sempre continuarão sendo 00.

Isso pode até afetar o design físico do sistema. Se o barramento de endereço precisar de 2 bits a menos, pode haver 2 pinos a menos na CPU e 2 traços a menos na placa de circuito.

Atomicidade

A CPU pode operar atomicamente em uma palavra de memória alinhada, o que significa que nenhuma outra instrução pode interromper essa operação. Isso é fundamental para a operação correta de muitas estruturas de dados sem bloqueio e outros paradigmas de simultaneidade .

Conclusão

O sistema de memória de um processador é um pouco mais complexo e envolvido do que o descrito aqui; uma discussão sobre como um processador x86 realmente aborda a memória pode ajudar (muitos processadores funcionam de maneira semelhante).

Há muitos outros benefícios em aderir ao alinhamento de memória que você pode ler neste artigo da IBM .

O uso principal de um computador é transformar dados. As arquiteturas e tecnologias modernas de memória foram otimizadas ao longo de décadas para facilitar a obtenção de mais dados, entrada, saída e entre mais e mais unidades de execução mais rápidas - de uma maneira altamente confiável.

Bônus: caches

Outro alinhamento por desempenho que eu aludi anteriormente é o alinhamento em linhas de cache que são (por exemplo, em algumas CPUs) 64B.

Para obter mais informações sobre quanto desempenho pode ser obtido aproveitando os caches, consulte a Galeria de efeitos de cache do processador ; desta pergunta sobre tamanhos de linha de cache

O entendimento das linhas de cache pode ser importante para certos tipos de otimizações de programa. Por exemplo, o alinhamento de dados pode determinar se uma operação toca uma ou duas linhas de cache. Como vimos no exemplo acima, isso pode facilmente significar que, no caso desalinhado, a operação será duas vezes mais lenta.

joshperry
fonte
as seguintes estruturas xyz têm tamanhos diferentes, porque a regra de cada membro deve começar com o endereço que é múltiplo de seu tamanho e o strcut deve terminar com endereço que é múltiplo do maior tamanho do membro da estrutura. struct x {short s; // 2 bytes e 2 tipos de preenchimento int i; // 4 bytes char c; // 1 bytes e 3 bytes de preenchimento long long l; }; struct y {int i; // 4 bytes char c; // 1 bytes e 1 byte de preenchimento short s; // 2 bytes}; struct z {int i; // 4 bytes short s; // 2 bytes char c; // 1 bytes e 1 byte de preenchimento};
Gavin
1
Se bem entendi, a razão pela qual um computador não consegue ler uma palavra desalinhada em uma única etapa é porque os endereços usam 30 bits e não 32 bits?
GetFree
1
@chux Sim, é verdade, o absoluto nunca se sustenta. O 8088 é um estudo interessante das vantagens e desvantagens entre velocidade e custo; era basicamente um 8086 de 16 bits (que tinha um barramento externo completo de 16 bits), mas com apenas metade das linhas de ônibus para economizar custos de produção. Por esse motivo, o 8088 precisava do dobro do ciclo do relógio para acessar a memória que o 8086, pois precisava fazer duas leituras para obter a palavra completa de 16 bits. A parte interessante, o 8086 pode fazer uma leitura de 16 bits alinhada por palavras em um único ciclo, as leituras desalinhadas levam 2. O fato de o 8088 ter um barramento de meia palavra mascarou essa desaceleração.
joshperry
2
@joshperry: Pequena correção: o 8086 pode fazer uma leitura de 16 bits alinhada por palavras em quatro ciclos, enquanto as leituras não alinhadas levam oito . Por causa da interface de memória lenta, o tempo de execução em máquinas baseadas em 8088 geralmente é dominado por buscas de instruções. Uma instrução como "MOV AX, BX" é nominalmente um ciclo mais rápido que "XCHG AX, BX", mas, a menos que seja precedida ou seguida por uma instrução cuja execução leva mais de quatro ciclos por byte de código, levará quatro ciclos a mais para executar. No 8086, a busca de código às vezes pode acompanhar a execução, mas no 8088, a menos que se use ... #
687
1
Muito verdade, @martin. Eu elaborei esses bytes de preenchimento para focar a discussão na estrutura, mas talvez fosse melhor incluí-los.
joshperry
22

você pode com alguns processadores ( o nehalem pode fazer isso ), mas anteriormente todo o acesso à memória era alinhado em uma linha de 64 bits (ou 32 bits), porque o barramento tem 64 bits de largura, era necessário buscar 64 bits por vez , e foi significativamente mais fácil buscá-los em 'pedaços' alinhados de 64 bits.

Portanto, se você quisesse obter um único byte, buscou o pedaço de 64 bits e mascarou os bits que não queria. Fácil e rápido se o seu byte estava na extremidade certa, mas se estivesse no meio desse bloco de 64 bits, você teria que mascarar os bits indesejados e depois transferir os dados para o local certo. Pior, se você quisesse uma variável de 2 bytes, mas que fosse dividida em 2 partes, seria necessário o dobro dos acessos à memória necessários.

Assim, como todos pensam que a memória é barata, eles apenas fizeram o compilador alinhar os dados nos tamanhos dos fragmentos do processador, para que seu código funcione mais rápido e com mais eficiência, com o custo da memória desperdiçada.

gbjbaanb
fonte
5

Fundamentalmente, o motivo é que o barramento de memória possui um comprimento específico muito, muito menor que o tamanho da memória.

Portanto, a CPU lê fora do cache L1 no chip, que costuma ter 32 KB atualmente. Mas o barramento de memória que conecta o cache L1 à CPU terá uma largura muito menor do tamanho da linha de cache. Isso será da ordem de 128 bits .

Assim:

262,144 bits - size of memory
    128 bits - size of bus

Os acessos desalinhados ocasionalmente se sobrepõem a duas linhas de cache, e isso exigirá uma leitura totalmente nova do cache para obter os dados. Pode até faltar todo o caminho para a DRAM.

Além disso, parte da CPU terá que ficar de cabeça para montar um único objeto dessas duas linhas de cache diferentes, cada uma com uma parte dos dados. Em uma linha, ele estará nos bits de ordem muito alta, na outra, nos bits de ordem muito baixa.

Haverá hardware dedicado totalmente integrado ao pipeline que manipula objetos alinhados nos bits necessários do barramento de dados da CPU, mas esse hardware pode estar ausente para objetos desalinhados, porque provavelmente faz mais sentido usar esses transistores para acelerar a otimização correta programas.

De qualquer forma, a segunda leitura de memória que às vezes é necessária desaceleraria o pipeline, independentemente de quanto hardware de propósito especial fosse (hipoteticamente e tolamente) dedicado a corrigir operações de memória desalinhadas.

DigitalRoss
fonte
5

@joshperry deu uma excelente resposta a esta pergunta. Além de sua resposta, tenho alguns números que mostram graficamente os efeitos que foram descritos, especialmente a amplificação 2X. Aqui está um link para uma planilha do Google mostrando como são os efeitos de diferentes alinhamentos de palavras. Além disso, aqui está um link para uma essência do Github com o código para o teste. O código do teste é adaptado do artigo escrito por Jonathan Rentzsch ao qual @joshperry referenciou. Os testes foram executados em um Macbook Pro com um processador Intel Core i7 de 64 bits e núcleo quádruplo de 2,8 GHz e 16 GB de RAM.

insira a descrição da imagem aqui

adino
fonte
4
O que significam xe ycoordenadas?
shuva 2/10/19
1
Qual geração Core i7? (Obrigado por postar links para código!)
Nick Desaulniers
2

Se um sistema com memória endereçável em bytes tiver um barramento de memória de 32 bits, significa que existem efetivamente quatro sistemas de memória em bytes que estão todos conectados para ler ou gravar o mesmo endereço. Uma leitura alinhada de 32 bits exigirá informações armazenadas no mesmo endereço nos quatro sistemas de memória, para que todos os sistemas possam fornecer dados simultaneamente. Uma leitura desalinhada de 32 bits exigiria que alguns sistemas de memória retornassem dados de um endereço e outros retornassem dados do próximo endereço superior. Embora existam alguns sistemas de memória otimizados para atender a essas solicitações (além do endereço, eles efetivamente possuem um sinal "mais um" que os leva a usar um endereço mais alto do que o especificado), esse recurso adiciona um custo considerável e complexidade para um sistema de memória;

supercat
fonte
2

Se você tiver um barramento de dados de 32 bits, as linhas de endereço do barramento de endereço conectadas à memória serão iniciadas em A 2 , de modo que apenas os endereços de 32 bits alinhados pode ser acessada em um único ciclo de ônibus.

Portanto, se uma palavra ultrapassar um limite de alinhamento de endereço - por exemplo, A 0 para dados de 16/32 bits ou A 1 para 32 bits de dados são não zero, dois ciclos do bus são necessários para obter os dados.

Algumas arquiteturas / conjuntos de instruções não suportam acesso não alinhado e geram uma exceção em tais tentativas; portanto, o código de acesso não alinhado gerado pelo compilador requer não apenas ciclos adicionais de barramento, mas instruções adicionais, tornando-o ainda menos eficiente.

Clifford
fonte
0

No PowerPC, você pode carregar um número inteiro de um endereço ímpar sem problemas.

Sparc e I86 e (acho) Itatnium levantam exceções de hardware quando você tenta isso.

Uma carga de 32 bits versus quatro cargas de 8 bits não fará muita diferença na maioria dos processadores modernos. Se os dados já estão no cache ou não, terá um efeito muito maior.

James Anderson
fonte
Em Sparc, este foi um "erro Bus", daí o capítulo "erro de ônibus, pegue o trem" em Peter Van der Linden "Programação Avançada C: Deep C Secrets"
jjg