Objectif de l'alignement de la mémoire

197

Certes, je ne comprends pas. Supposons que vous ayez une mémoire avec un mot mémoire d'une longueur de 1 octet. Pourquoi ne pouvez-vous pas accéder à une variable longue de 4 octets en un seul accès mémoire sur une adresse non alignée (c'est-à-dire non divisible par 4), comme c'est le cas avec des adresses alignées?

arche
la source
17
Après avoir fait quelques recherches supplémentaires sur Google, j'ai trouvé ce lien génial, qui explique très bien le problème.
ark
Consultez ce petit article pour les personnes qui commencent à apprendre ceci: blog.virtualmethodstudio.com/2017/03/memory-alignment-run-fools
darkgaze
3
@ark link broken
John Jiang
2
@JohnJiang Je pense avoir trouvé le nouveau lien ici: developer.ibm.com/technologies/systems/articles/pa-dalign
ejohnso49

Réponses:

63

C'est une limitation de nombreux processeurs sous-jacents. Cela peut généralement être contourné en effectuant 4 récupérations inefficaces d'un octet plutôt qu'une extraction de mot efficace, mais de nombreux spécificateurs de langage ont décidé qu'il serait plus facile de les interdire et de forcer tout à être aligné.

Il y a beaucoup plus d'informations dans ce lien que l'OP a découvertes.

Paul Tomblin
la source
311

Le sous-système de mémoire sur un processeur moderne est limité à l'accès à la mémoire avec la granularité et l'alignement de sa taille de mot; c'est le cas pour plusieurs raisons.

La vitesse

Les processeurs modernes ont plusieurs niveaux de mémoire cache dans lesquels les données doivent être extraites; la prise en charge des lectures à un octet rendrait le débit du sous-système de mémoire étroitement lié au débit de l'unité d'exécution (aka lié au processeur); tout cela rappelle à quel point le mode PIO a été dépassé par DMA pour plusieurs des mêmes raisons dans les disques durs.

Le processeur lit toujours à sa taille de mot (4 octets sur un processeur 32 bits), donc lorsque vous effectuez un accès d'adresse non aligné - sur un processeur qui le prend en charge - le processeur va lire plusieurs mots. Le CPU lira chaque mot de mémoire que votre adresse demandée chevauche. Cela provoque une amplification jusqu'à 2 fois le nombre de transactions mémoire nécessaires pour accéder aux données demandées.

Pour cette raison, il peut très facilement être plus lent de lire deux octets que quatre. Par exemple, disons que vous avez une structure en mémoire qui ressemble à ceci:

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

Sur un processeur 32 bits, il serait très probablement aligné comme indiqué ici:

Disposition de structure

Le processeur peut lire chacun de ces membres en une seule transaction.

Supposons que vous ayez une version compressée de la structure, peut-être du réseau où elle était emballée pour l'efficacité de la transmission; cela pourrait ressembler à ceci:

Struct emballé

La lecture du premier octet sera la même.

Lorsque vous demandez au processeur de vous donner 16 bits à partir de 0x0005, il devra lire un mot à partir de 0x0004 et décaler vers la gauche de 1 octet pour le placer dans un registre 16 bits; un peu de travail supplémentaire, mais la plupart peuvent gérer cela en un seul cycle.

Lorsque vous demandez 32 bits à partir de 0x0001, vous obtenez une amplification 2X. Le processeur lira de 0x0000 dans le registre de résultat et décale 1 octet à gauche, puis relit à partir de 0x0004 dans un registre temporaire, décale 3 octets à droite, puisOR avec le registre de résultat.

Intervalle

Pour un espace d'adressage donné, si l'architecture peut supposer que les 2 LSB sont toujours à 0 (par exemple, des machines 32 bits) alors elle peut accéder à 4 fois plus de mémoire (les 2 bits sauvegardés peuvent représenter 4 états distincts), soit la même quantité de mémoire avec 2 bits pour quelque chose comme des drapeaux. Enlever les 2 LSB d'une adresse vous donnerait un alignement de 4 octets; également appelé une foulée de 4 octets. Chaque fois qu'une adresse est incrémentée, elle incrémente effectivement le bit 2 et non le bit 0, c'est-à-dire que les 2 derniers bits continueront toujours à l'être 00.

Cela peut même affecter la conception physique du système. Si le bus d'adresse a besoin de 2 bits de moins, il peut y avoir 2 broches de moins sur le CPU et 2 traces de moins sur le circuit imprimé.

Atomicité

Le CPU peut fonctionner sur un mot de mémoire aligné de manière atomique, ce qui signifie qu'aucune autre instruction ne peut interrompre cette opération. Ceci est essentiel au bon fonctionnement de nombreuses structures de données sans verrouillage et d'autres paradigmes de concurrence .

Conclusion

Le système de mémoire d'un processeur est un peu plus complexe et complexe que celui décrit ici; une discussion sur la manière dont un processeur x86 traite réellement la mémoire peut aider (de nombreux processeurs fonctionnent de la même manière).

Le respect de l'alignement de la mémoire présente de nombreux autres avantages que vous pouvez lire dans cet article IBM .

L'utilisation principale d'un ordinateur est de transformer des données. Les architectures et technologies de mémoire modernes ont été optimisées au fil des décennies pour faciliter l'obtention d'un plus grand nombre de données, d'entrée, de sortie et entre des unités d'exécution plus nombreuses et plus rapides, de manière hautement fiable.

Bonus: Caches

Un autre alignement pour les performances auquel j'ai fait allusion précédemment est l'alignement sur les lignes de cache qui sont (par exemple, sur certains processeurs) 64B.

Pour plus d'informations sur les performances pouvant être obtenues en exploitant les caches, jetez un œil à la Galerie des effets du cache du processeur ; de cette question sur les tailles des lignes de cache

La compréhension des lignes de cache peut être importante pour certains types d'optimisations de programme. Par exemple, l'alignement des données peut déterminer si une opération touche une ou deux lignes de cache. Comme nous l'avons vu dans l'exemple ci-dessus, cela peut facilement signifier que dans le cas mal aligné, l'opération sera deux fois plus lente.

Joshperry
la source
les structures suivantes xyz ont des tailles différentes, en raison de la règle de chaque membre doit commencer par l'adresse qui est un multiple de sa taille et le strcut doit se terminer par une adresse qui est un multiple de la plus grande taille du membre de la structure. struct x {short s; // 2 octets et 2 tytes de remplissage int i; // 4 octets char c; // 1 octet et 3 octets de remplissage long long l; }; struct y {int i; // 4 octets char c; // 1 octet et 1 octet de remplissage courts s; // 2 octets}; struct z {int i; // 4 octets courts s; // 2 octets char c; // 1 octet et 1 octet de remplissage};
Gavin
1
Si je comprends bien, la raison pour laquelle un ordinateur ne peut pas lire un mot non aligné en une seule étape est que les addesses utilisent 30 bits et non 32 bits ??
GetFree
1
@chux Oui c'est vrai, les absolus ne tiennent jamais. Le 8088 est une étude intéressante des compromis entre vitesse et coût, il s'agissait essentiellement d'un 8086 16 bits (qui avait un bus externe complet de 16 bits) mais avec seulement la moitié des lignes de bus pour réduire les coûts de production. Pour cette raison, le 8088 avait besoin de deux fois plus de cycles d'horloge pour accéder à la mémoire que le 8086 car il devait faire deux lectures pour obtenir le mot de 16 bits complet. La partie intéressante, le 8086 peut faire une lecture de 16 bits alignée par mot en un seul cycle, les lectures non alignées en prennent 2. Le fait que le 8088 ait un bus demi-mot a masqué ce ralentissement.
joshperry
2
@joshperry: Légère correction: le 8086 peut effectuer une lecture 16 bits alignée sur mot en quatre cycles, tandis que les lectures non alignées en prennent huit . En raison de la lenteur de l'interface de mémoire, le temps d'exécution sur les machines basées sur 8088 est généralement dominé par les extractions d'instructions. Une instruction comme "MOV AX, BX" est nominalement un cycle plus rapide que "XCHG AX, BX", mais à moins qu'elle ne soit précédée ou suivie d'une instruction dont l'exécution prend plus de quatre cycles par octet de code, il faudra quatre cycles de plus pour exécuter. Sur le 8086, la récupération de code peut parfois suivre l'exécution, mais sur le 8088 à moins que l'on utilise ...
supercat
1
Très vrai, @martin. J'ai élidé ces octets de remplissage pour concentrer la discussion intra-structure, mais il serait peut-être préférable de les inclure.
joshperry
22

vous pouvez avec certains processeurs ( le nehalem peut le faire ), mais auparavant tous les accès mémoire étaient alignés sur une ligne 64 bits (ou 32 bits), car le bus a une largeur de 64 bits, vous deviez récupérer 64 bits à la fois , et il était beaucoup plus facile de les récupérer en «morceaux» alignés de 64 bits.

Donc, si vous vouliez obtenir un seul octet, vous avez récupéré le bloc 64 bits, puis masqué les bits que vous ne vouliez pas. Facile et rapide si votre octet était à la bonne extrémité, mais s'il était au milieu de ce bloc de 64 bits, vous devrez masquer les bits indésirables, puis déplacer les données au bon endroit. Pire encore, si vous vouliez une variable de 2 octets, mais qui était divisée en 2 morceaux, cela nécessitait le double des accès mémoire requis.

Ainsi, comme tout le monde pense que la mémoire est bon marché, ils ont simplement obligé le compilateur à aligner les données sur la taille des blocs du processeur afin que votre code s'exécute plus rapidement et plus efficacement au prix d'une mémoire gaspillée.

gbjbaanb
la source
5

Fondamentalement, la raison est que le bus mémoire a une longueur spécifique qui est beaucoup plus petite que la taille de la mémoire.

Ainsi, le processeur lit le cache L1 sur puce, qui fait souvent 32 Ko de nos jours. Mais le bus mémoire qui connecte le cache L1 au CPU aura la largeur beaucoup plus petite de la taille de la ligne de cache. Ce sera de l'ordre de 128 bits .

Alors:

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

Les accès mal alignés chevaucheront parfois deux lignes de cache, ce qui nécessitera une lecture de cache entièrement nouvelle afin d'obtenir les données. Il pourrait même manquer tout le chemin vers la DRAM.

De plus, une partie de la CPU devra se tenir debout sur sa tête pour assembler un seul objet parmi ces deux lignes de cache différentes qui contiennent chacune une partie des données. Sur une ligne, ce sera dans les bits d'ordre très élevé, dans l'autre, les bits d'ordre très bas.

Il y aura du matériel dédié entièrement intégré dans le pipeline qui gère le déplacement des objets alignés sur les bits nécessaires du bus de données du processeur, mais un tel matériel peut manquer pour les objets mal alignés, car il est probablement plus logique d'utiliser ces transistors pour accélérer correctement optimisé programmes.

Dans tous les cas, la deuxième lecture de mémoire parfois nécessaire ralentirait le pipeline, quelle que soit la quantité de matériel spécialisé (hypothétiquement et stupidement) dédié à la correction des opérations de mémoire mal alignées.

DigitalRoss
la source
5

@joshperry a donné une excellente réponse à cette question. En plus de sa réponse, j'ai quelques chiffres qui montrent graphiquement les effets qui ont été décrits, en particulier l'amplification 2X. Voici un lien vers une feuille de calcul Google montrant à quoi ressemble l'effet des différents alignements de mots. De plus, voici un lien vers un Github avec le code du test. Le code du test est adapté de l'article écrit par Jonathan Rentzsch auquel @joshperry a fait référence. Les tests ont été exécutés sur un Macbook Pro avec un processeur quadricœur Intel Core i7 64 bits à 2,8 GHz et 16 Go de RAM.

entrez la description de l'image ici

adino
la source
4
Que faire xet ycoordonnées signifient?
shuva
1
Quelle génération Core i7? (Merci d'avoir publié des liens vers le code!)
Nick Desaulniers
2

Si un système avec une mémoire adressable par octets a un bus mémoire de 32 bits de large, cela signifie qu'il existe effectivement des systèmes de mémoire de quatre octets qui sont tous câblés pour lire ou écrire la même adresse. Une lecture alignée sur 32 bits nécessitera des informations stockées dans la même adresse dans les quatre systèmes de mémoire, de sorte que tous les systèmes peuvent fournir des données simultanément. Une lecture 32 bits non alignée nécessiterait que certains systèmes de mémoire retournent des données à partir d'une adresse et certains renvoient des données à partir de l'adresse suivante supérieure. Bien que certains systèmes de mémoire soient optimisés pour pouvoir répondre à ces demandes (en plus de leur adresse, ils ont effectivement un signal «plus un» qui les amène à utiliser une adresse supérieure à celle spécifiée), une telle fonctionnalité ajoute un coût considérable et la complexité d'un système de mémoire;

supercat
la source
2

Si vous disposez d'un bus de données 32 bits, les lignes d'adresse du bus d'adresses connectées à la mémoire commenceront à partir de A 2 , de sorte que seules les adresses alignées 32 bits peuvent être accédées en un seul cycle de bus.

Donc, si un mot couvre une limite d'alignement d'adresse - c'est-à-dire A 0 pour des données 16/32 bits ou A 1 pour les données 32 bits n'est pas égal à zéro, deux cycles de bus sont nécessaires pour obtenir les données.

Certaines architectures / jeux d'instructions ne prennent pas en charge l'accès non aligné et généreront une exception sur de telles tentatives, de sorte que le code d'accès non aligné généré par le compilateur nécessite non seulement des cycles de bus supplémentaires, mais des instructions supplémentaires, ce qui le rend encore moins efficace.

Clifford
la source
0

Sur PowerPC, vous pouvez charger un entier à partir d'une adresse impaire sans problème.

Sparc et I86 et (je pense) Itatnium lèvent des exceptions matérielles lorsque vous essayez ceci.

Une charge 32 bits contre quatre charges 8 bits ne fera pas beaucoup de différence sur la plupart des processeurs modernes. Le fait que les données soient déjà en cache ou non aura un effet beaucoup plus important.

James Anderson
la source
Sur Sparc, il s'agissait d'une "erreur de bus", d'où le chapitre "Erreur de bus, prenez le train" dans "Expert C Programming: Deep C Secrets" de Peter Van der Linden
jjg