11
votes

Alignement le long des limites de 4 octets

J'ai récemment pensé à penser à l'alignement ... C'est quelque chose que nous n'avons pas à prendre en compte, mais j'ai compris que certains processeurs exigent que des objets soient alignés sur des limites de 4 octets. Qu'est-ce que cela signifie exactement et quels systèmes spécifiques ont des exigences d'alignement?

Supposons que j'ai un pointeur arbitraire:

non signé Char * PTR

Maintenant, j'essaie de récupérer une double valeur d'un emplacement de mémoire:

double d = ** ((double *) PTR);

Est-ce que cela va causer des problèmes?


5 commentaires

Notez que les doubles peuvent très bien avoir la taille d'un alignement (double), qui peut être à son tour> et les types avec Tailleof (T) <4 ne disposent jamais d'alignements sur 4 limites d'octets - sinon vous ne pouviez pas aligner les deux éléments d'un T [ 2]!


J'essaie d'imaginer quel type de conception de programme aurait besoin de vous pour lire des doubles des pointeurs arbitraires non alignés. Je ne peux pas penser à un scénario pratique - du moins, pour tout scénario, il existe de meilleures solutions qui n'ont pas de problèmes d'alignement et sont plus susceptibles de coder multiplateformes.


Tant que les points de PTR à la mémoire allouée de manière dynamique, cela fonctionnera. Si PTR pointe sur une matrice statique (globale ou locale), il n'y a pas de garantie. (Voir ma réponse ci-dessous pour plus de détails)


Pouvons-nous rendre le pointeur aligné sur une limite de 4 octets de manière efficace?


La chose à considérer ici est que certaines ISA (E.G. X86) vous permettent de charger la mémoire dans un registre 32 bits à l'aide d'une adresse 32 bits alignée à l'aide d'une instruction de charge ordinaire. La plupart des ISAS de RISC ne le font pas et ne nécessitent que le compilateur émet des instructions supplémentaires pour effectuer 2 charges, ainsi qu'un peu-twidding bit pour toutes les données qui chevauchent la limite. Il ne fonctionnera généralement pas bien (le meilleur cas) sur l'un des types d'architecture, en supposant que le compilateur reconnaît la situation. Si le compilateur n'est pas au courant de l'accès non aligné (pire des cas), il fonctionnera sur x86 mais pas d'autres.


9 Réponses :


1
votes

Un exemple d'exigence d'alimentation est lorsque vous utilisez des instructions de vectorisation (SIMD). (Il peut être utilisé sans alimentation mais est beaucoup plus rapide si vous utilisez une sorte d'instruction nécessitant un alignement).


0 commentaires

21
votes

Il peut certainement causer des problèmes sur certains systèmes.

Par exemple, sur les systèmes basés sur les bras, vous ne pouvez pas aborder un mot de 32 bits qui n'est pas aligné sur une limite de 4 octets. Cela entraînera une exception d'une violation d'accès. Sur x86, vous pouvez accéder à de telles données non alignées, bien que la performance souffre un peu puisque deux mots doivent être récupérés de la mémoire au lieu d'une seule.


3 commentaires

Certains systèmes de bras accèdent même silencieusement l'adresse alignée correspondante dans laquelle les bits inférieurs sont nuls, ce qui peut entraîner du mal à trouver des bugs.


Il s'agit définitivement d'un problème sur le bras si des emplacements d'octets arbitraires sont utilisés comme Laulto et Starblue Signal. Mais les blocs de mémoire alloués auront toujours un alignement suffisant (I.e. 16 octets), même s'ils sont utilisés pour des matrices de caractères. Faites également attention au MSB / LSB lors de la traversée des plates-formes avec cette technique.


apparemment bras V6 (généralement) et ci-dessus (toujours) définir des accès non alignés à dire à faire La chose x86 / x64 maintenant, à l'exception de LDM / STM et peut-être d'autres exceptions non "notables".



4
votes

L'alignement affecte la disposition des structures. Considérons cette structure: xxx

sur un processeur 32 bits La mise en page de cette structure sera souvent la suivante: xxx

l'exigence est qu'un La valeur 32 bits doit être alignée sur une limite 32 bits. Si la structure est modifiée comme ceci: xxx

La mise en page sera ceci: xxx

la valeur 16 bits est aligné sur une limite de 16 bits.

Parfois, vous voulez pack les structures peut-être si vous souhaitez correspondre à la structure avec un format de données. En utilisant une option de compilateur ou peut-être un #pragma , vous pouvez supprimer l'espace excédent: xxx

Cependant, accédez à un membre non aligné d'un emballé Les structures seront souvent beaucoup plus lentes sur les CPU modernes, ou peuvent même entraîner une exception.


1 commentaires

Pour une bonne programmation multiplate-forme, vous ne voudriez probablement pas "faire correspondre la structure avec un format de données". À moins que le format de données ait été conçu de manière à ce que tous les membres soient alignés (par exemple, des protocoles TCP / IP, donc j'ai entendu), mais vous avez toujours des problèmes d'endansion.



3
votes

Oui, cela pourrait causer des problèmes.

4-alignement signifie simplement que le pointeur, lorsqu'il est considéré comme une adresse numérique, est un multiple de 4. Si le pointeur n'est pas un multiple de l'alignement requis, il est alors non aligné. Il y a deux raisons pour lesquelles des compilateurs placent des restrictions d'alignement sur certains types: p>

  1. Parce que le matériel ne peut pas charger ce type de données d'un pointeur non aligné (au moins, sans utiliser les instructions que le compilateur veut émettre des charges et des magasins). LI>
  2. Parce que le matériel charge que DataType plus rapidement des pointeurs alignés. LI> ol>

    Si vous êtes au cas où (1) et le double est 4-aligné et que vous essayez votre code avec un pointeur char * code> qui n'est pas 4-aligné, alors vous 'll obtiendra probablement un piège au matériel. Certains matériels ne piègent pas. Il charge juste une valeur de non-sens et continue. Cependant, la norme C ++ ne définit pas ce qui peut se produire (comportement non défini), ce code pourrait donc définir votre ordinateur en feu. P>

    sur x86, vous n'êtes jamais au cas (1), car la norme Les instructions de charge peuvent gérer des pointeurs non alignés. Sur le bras, il n'y a pas de charges non alignées et si vous en essayez un problème, votre programme se bloque (si vous avez de la chance. Certains bras échouent silencieusement). P>

    revenir à votre exemple, la question est la question de la question. J'essaye cela avec un char * code> qui n'est pas 4 aligné. Si vous avez écrit avec succès un double là via un double * code>, vous pourrez le lire. Donc, si vous aviez à l'origine un pointeur "approprié" à doubler, que vous avez lancé vers Char * Code> et que vous coulez maintenant, vous n'avez pas à vous soucier de l'alignement. P>

    Mais vous avez dit arbitraire char * code>, alors je suppose que ce n'est pas ce que vous avez. Si vous lisez une partie de données d'un fichier, qui contient un double sérialisé, vous devez em> em> vous assurez que les exigences d'alignement de votre plate-forme sont remplies afin de faire cette distribution. Si vous avez 8 octets représentant un double format de fichier, vous ne pouvez pas simplement le lire Willy-Nilly dans un tampon de char * à tout moment décalé puis jeté sur double * p> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P> P > Le moyen le plus simple de le faire est de vous assurer de lire les données de fichier dans une structure appropriée. Vous avez également contribué au fait que les allocations de mémoire sont toujours alignées sur l'exigence d'alignement maximale de tout type dont ils sont suffisamment importants. Donc, si vous allouez un tampon assez gros pour contenir un double, le début de ce tampon a tout l'alignement requis par le double. Ainsi, vous pouvez lire les 8 octets représentant le double au début du tampon, lancer (ou utiliser un syndicat) et lire la double sortie. P>

    Alternativement, vous pouvez faire quelque chose comme ceci: P >

    double readUnalignedDouble(char *un_ptr) {
        double d;
        // either of these
        std::memcpy(&d, un_ptr, sizeof(d));
        std::copy(un_ptr, un_ptr + sizeof(d), reinterpret_cast<char *>(&d));
        return d;
    }
    


1 commentaires

Considérez par exemple, Char * P = Nouveau Char [100]; Char * PTR = P + 1; PTR est maintenant non aligné si le double est 4-aligné. Casting PTR sur Double * Ensuite, la lecture d'un double est un comportement non défini (même si vous avez défini p [1] via p [taille de la taille (double)] à 0).



2
votes

sur le X86, il va toujours courir, bien sûr plus efficacement lorsqu'il est aligné.

Mais si vous êtes multithreading, surveillez la lecture en lecture-écriture. Avec une valeur de 64 bits, vous avez besoin d'une machine X64 pour vous donner des lectures atomiques entre les filets.
Si vous dites que vous lisez la valeur d'un autre fil lorsqu'il s'agit d'incrémenter entre 0x0000000000.FFFFFFFF et 0x00000001.00000000, un autre thread pourrait en théorie lire stire 0 ou 1ffffffff, surtout si la valeur a chevauché une limite de cache-ligne.
Je recommande "la programmation simultanée simultanée de Duffy" pour sa jolie discussion sur les modèles de mémoire, mentionnant même les gothas d'alignement sur des multipractoristes lorsque DOT-Net fait un GC. Vous voulez rester à l'écart de l'itanium!


1 commentaires

Les valeurs alignées 64 bits peuvent être accessibles atomiquement sur X86 32 bits avec CMPXCHG8B, en fait. Il y a aussi un CMPXCH16B correspondant de 128 bits sur 64 bits également.



13
votes

Voici ce que Manuel de référence Intel X86 / X64 dit sur les alignements:

4.1.1 Alignement des mots, mots doubles, quadwoutes et double quadwords

mots, mots doubles et quadwords font pas besoin d'être aligné en mémoire sur frontières naturelles. Le naturel limites pour les mots, deux mots doubles, et les quadruples sont même numérotés adresses, adresses uniformément divisible par quatre et traite uniformément divisible par huit, respectivement. Cependant, pour améliorer la performance de programmes, structures de données (surtout piles) devrait être aligné sur naturel limites chaque fois que possible. Les la raison pour cela est que le processeur nécessite deux accès à la mémoire pour faire un accès à la mémoire non alignée; aligné Les accès ne nécessitent qu'une seule mémoire accès. Un mot ou double motorisation qui traverse une limite de 4 octets ou un quaddwout opérande qui traverse un La limite de 8 octets est considérée non aligné et nécessite deux distincts Cycles de bus de mémoire pour l'accès.

Quelques instructions qui fonctionnent sur Double quadruples nécessitent une mémoire opérandes à être alignés sur un naturel frontière. Ces instructions génèrent une exception de protection générale (#gp) Si un opérande non aligné est spécifié. Une limite naturelle pour un double Quadword est une adresse uniformément divisible par 16. Autres instructions qui fonctionnent sur des doubles quadruples permettre un accès non aligné (sans générer une protection générale exception). Cependant, une mémoire supplémentaire Les cycles de bus sont nécessaires pour accéder données non alignées de la mémoire.

N'oubliez pas, les manuels de référence sont la source ultime d'informations sur le développeur et l'ingénieur responsables. Si vous avez affaire à quelque chose de bien documenté, tel que Intel CPus, il suffit de rechercher ce que le manuel de référence indique à propos de la question.


2 commentaires

@onebyone: True, mais d'autres architectures ont également leurs propres manuels de référence.


Oui, je veux dire que parfois vous voulez écrire du code qui n'est pas pour une architecture particulière (en fait, cela a été le cas habituel pour moi jusqu'à présent). Dans cette situation, les manuels de référence de la CPU ne vous aident pas, vous ne pouvez compter que sur la norme C ++.



4
votes

Oui, cela peut causer un certain nombre de problèmes. La norme C ++ ne garantit pas réellement que cela fonctionnera. Vous ne pouvez pas simplement lancer arbitrairement entre les types de pointeur.

Lorsque vous lancez un pointeur de caractère sur un double pointeur, il utilise un REINIERPRET_CAST , qui applique un Mappage défini défini par la mise en œuvre. Vous n'êtes pas garanti que le pointeur résultant contiendra le même motif de bits, ou qu'il indiquera la même adresse ou, bien, autre chose. En termes plus pratiques, vous n'êtes pas non plus garanti que la valeur que vous lisez est alignée correctement. Si les données étaient écrites comme une série de caractères, ils utiliseront les exigences d'alignement de Char.

Quels moyens d'alignement, essentiellement seulement que l'adresse de départ de la valeur doit être divisible par la taille d'alignement. L'adresse 16 est alignée sur les limites des 1, 2, 4, 8 et 16 octets, par exemple, de sorte que les valeurs typiques de la CPU, de ces tailles peuvent être stockées là-bas.

L'adresse 6 n'est pas alignée sur une limite de 4 octets, nous ne devrions donc pas stocker des valeurs de 4 octets là-bas.

Il convient de noter que même sur les CPU qui n'applique pas ou ne nécessitent pas d'alignement, vous obtenez généralement un ralentissement important d'accéder à des valeurs non alignées.


0 commentaires

1
votes

L'alignement de la mémoire appliquée est beaucoup plus courant dans Architectures basés sur RISC tels que MIPS.
La pensée principale de ces types de transformateurs, Afaik, est vraiment un problème de vitesse.
La méthodologie RISC consistait à avoir un ensemble d'instructions simples et rapides (généralement un cycle de mémoire par instruction). Cela ne signifie pas nécessairement que cela dispose de moins d'instructions qu'un processeur de CSCC, plus qu'il a plus simple, des instructions plus rapides.
De nombreux processeurs MIPS, bien que 8 octets adressables soient alignés (32 bits typiquement mais pas toujours), puis masquez les bits appropriés.
L'idée étant que cela est plus rapide de faire un masque de charge en charge + aligné que d'essayer de faire une charge non alignée. En règle générale (et bien sûr, cela dépend vraiment du chipset), faire une charge non alignée générerait une erreur de bus afin que les processeurs RISC offrent une instruction «chargée / stockage inalignée», mais cela serait souvent beaucoup plus lent que la charge / magasin alignée correspondant. .

Bien sûr, cela ne répond toujours pas à la question de savoir pourquoi ils font cela, c'est quel avantage le mot de mémoire est-il aligné? Je ne suis pas un expert matériel et je suis sûr que quelqu'un ici peut donner une meilleure réponse, mais mes deux meilleures suppositions sont:
1. Il peut être beaucoup plus rapide d'aller chercher du cache lorsque le mot aligné parce que de nombreuses caches sont organisées en lignes de cache (n'importe quoi de 8 à 512 octets) et comme la mémoire cache est typiquement beaucoup plus chère que la RAM, vous voulez faire le meilleur de celui-ci.
2. Il peut être beaucoup plus rapide d'accéder à chaque adresse de la mémoire car elle vous permet de lire via «Mode Burst» (c'est-à-dire la recherche de l'adresse séquentielle suivante avant qu'elle ne soit nécessaire)

Remarque Aucune de ce qui précède n'est strictement impossible avec les magasins non alignés, je suppose que je devine (bien que je ne sache pas) que beaucoup d'entre elles reviennent aux choix de conception du matériel et au coût


1 commentaires

Voulez-vous dire "octet adresse" au lieu de "8 octets adressable"?



2
votes

SPARC (Machines Solaris) est une autre architecture (au moins quelques-unes dans le passé) qui va étouffer (donner une erreur Sigbus) si vous essayez d'utiliser une valeur non alignée.

Un addendum à Martin York, Malloc est également aligné sur le plus grand type possible, c'est-à-dire qu'il est sûr pour tout, comme "nouveau". En fait, fréquemment «nouveau» utilise simplement Malloc.


0 commentaires