8
votes

Que prendre en compte en ce qui concerne l'alignement lors de la conception d'un pool de mémoire?

Je travaille sur une piscine mémoire pour un petit moteur de jeu.

L'utilisation principale sera sous forme de stockage distinct; Une piscine contient un objet d'un type et d'une taille spécifiques. Actuellement, les piscines peuvent être utilisées pour stocker quoi que ce soit, mais les allocations seront effectuées en blocs d'une taille spécifique. La plupart des besoins de la mémoire seront attribués à la fois, mais «la prolifération» peut être activé si nécessaire pour faciliter le réglage (taille presque fixe).

Problème est que j'ai commencé à obtenir un peu paranoïaque lors de la contemplation de l'alignement de la mémoire. Je n'ai utilisé que la gestion de la mémoire brute sur les processeurs 8 bits où tout est aligné sur des octets.

Je laisse l'utilisateur (moi) spécifier la taille souhaitée des blocs, qui dans le boîtier de stockage distinct serait la taille des objets que je vais stocker.

L'approche actuelle consiste à allouer une pièce de mémoire blocs * (souhaité_size + header_size) gros et placez les objets dedans, avec un en-tête pour chaque bloc; Les objets seraient évidemment positionnés directement derrière cet en-tête.

Que dois-je envisager en ce qui concerne l'alignement de la mémoire dans mon scénario?

La réponse que j'ai proposée jusqu'à présent est que tant que souhaité_size représente les données alignées n -byte; L'en-tête est correctement aligné et emballé par le compilateur ainsi que les données réelles, tout stocké dans le bloc sera n -byte aligné.

n est quelle que soit la limite requise par la plate-forme. Je vis me ciblant x86 pour le moment, mais je n'aime pas faire d'hypothèses sur la plate-forme de mon code.

Certaines des ressources que j'ai utilisées:


0 commentaires

5 Réponses :


2
votes

Votre compilateur alignera déjà les membres d'objets et de structures qui seront stockés dans la piscine. En utilisant l'emballage par défaut qui convient à votre architecture, généralement 8 pour un noyau 32 bits. Il vous suffit de vous assurer que l'adresse que vous générez de votre piscine est alignée en conséquence. Qui sur un système d'exploitation de 32 bits devrait être un multiple de 8 octets.

Mauvaise alignement Les objets peuvent être très coûteux lorsqu'ils traversent une limite de la ligne de cache de la CPU. Un double qui chevauche deux lignes de cache prend autant que trois fois moins de lecture ou écrit.



8
votes

Allocations avec MALLOC est garantie pour être aligné pour tout type fourni par le compilateur et donc n'importe quel objet [*].

Le danger est que votre en-tête a une plus petite exigence d'alignement que la condition d'alignement maximale de votre implémentation. Ensuite, sa taille pourrait ne pas être un multiple du max. alignement, et donc lorsque vous essayez de lancer / utiliser buf + header_size comme un pointeur sur quelque chose qui fait a le max. alignement, il est mal aligné. Aussi loin que C est concerné, c'est un comportement indéfini. Sur Intel ça marche mais est plus lent. Sur certains bras, cela provoque une exception matérielle. Sur certains bras, il donne silencieusement la mauvaise réponse. Donc, si vous ne voulez pas faire des hypothèses sur la plate-forme de votre code, vous devez y faire face.

Il y a essentiellement trois astuces que je suis au courant de vous assurer que votre en-tête ne cause pas de désalignement:

  • Utilisez un pragma d'alignement spécifique à la mise en œuvre pour forcer le problème.
  • Utilisez une connaissance spécifique à la plate-forme de la disposition de la structure et de l'alignement afin de vous assurer que sa taille est donc un multiple de l'exigence d'alignement maximum sur le système. Typiquement, cela signifie: "Collez un int en tant que rembourrage si nécessaire pour en faire un 8 multiple plutôt que juste un 4-multiple".
  • Faites l'en-tête un Union de chaque type standard, ainsi que la structure que vous souhaitez réellement utiliser. Fonctionne bien en C, mais vous auriez des problèmes dans C ++ si votre en-tête n'est pas valable pour l'adhésion des syndicats.

    Alternativement, vous pouvez simplement définir en-tête_size ne pas être Tailleof (en-tête) , mais pour être cette taille arrondie à un multiple d'une puissance chunky de 2 qui est " assez bien". Si vous perdez un peu de mémoire, alors soyez-le, et vous pouvez toujours avoir un «en-tête de portabilité» qui définit ce genre de chose d'une manière qui n'est pas purement indépendante de la plate-forme, mais facilite l'adaptation à de nouvelles plateformes.

    [*] avec une exception commune étant des types SIMD sur la taille supérieure. Depuis qu'ils sont non standard, et il serait inutile de 16 aligner toutes les allocations à peine à cause d'eux, ils ont fait de côté à la main, et vous avez besoin de fonctions d'allocation spéciales pour eux.


8 commentaires

Peut-on aussi ajouter que la même chose est vraie avec toutes les fonctions d'allocation (donc :: opérateur neuf et kin.)


Merci d'avoir répondu. Comment déterminer le max. alignement d'un type? Si j'ai bien compris votre deuxième paragraphe - Supposons X86 ici pour la simplicité - cela signifie que si l'en-tête est de 4 octets, il s'agira de 4 octets alignés et que les données sont de 8 octets importants, 8 octets seront alignés. Cela signifie que j'ai besoin d'en-tête de patin avec 4 octets pour rendre les données correctement alignées, correctes?


J'ai pensé à votre proposition "assez bonne", cela semble pragmatique; J'aime pragmatique. Comment savoir ce qui est assez bon, cela permettrait-il simplement que l'en-tête est aligné sur le max. Alignement de la plate-forme ou dépendrait-il de la taille réelle des données stockées (le rendant moins souhaitable)?


Chaque type sur une plate-forme donnée a un alignement imposé par le compilateur (au moins, dans les cas où aucun pragma ou attribut d'emballage n'indique le contraire). Vous devez savoir quels sont ces alignements et trouver le plus grand. L'allocation , et donc l'en-tête elle-même puisque vous le placez au début de l'allocation, aura cet alignement. Le suivant octet après L'en-tête aura cet alignement maximum si (et seulement si) la taille de l'en-tête est un multiple du max. alignement.


En ce qui concerne "assez bon" - la taille du plus gros type sur votre plate-forme est "assez bonne", car rien ne peut avoir une condition d'alignement plus grosse que sa taille.


Merci, je pense que je saisis cela maintenant. Cela signifierait que si l'alignement maximum de la plate-forme était 8, mais l'en-tête occupait quelque chose comme 14 octets, je supposerais simplement que l'en-tête était de 16 octets importants, car cela positionnait correctement les données du bloc.


C'est vrai: laissez 16 octets pour cela, dont il occupera 14 et les 2 autres sont inutilisés. Bien qu'il soit improbable que votre tête ait une taille 14, car elle aura probablement quelque chose qui nécessite un alignement 4, de sorte que la taille doit être un multiple de 4. C'est 12 que vous voulez vraiment regarder et arrondir.


Merci pour l'aide, j'aurais dû tout ce que je dois faire au moins le cas de l'utilisation de base fonctionne maintenant.



1
votes

Utilisez http://msdn.microsoft.com/en-us/ Bibliothèque / BB982233.ASPX - STD :: Alignment_of. Cela garantit que, pour chaque t, vous allez dans votre piscine, vous connaîtrez l'alignement et vous assurera que cela convient. Je ne vais pas prétendre savoir / comprendre la logique réelle que vous utiliseriez pour effectuer ces informations en garanties d'alignement, mais elle existe et est disponible pour une utilisation.


1 commentaires

Merci pour la réponse, il sera probablement utile.



1
votes

Aussi loin que je sache Boost :: La piscine est basée sur "l'allocator des petits objets" d'Alexandrescu, expliqué dans son livre "Moderne C ++ Design". Je dois lire le livre (puisque vous écrivez ce produit pour apprendre aussi)


1 commentaires

Merci, va voir si je peux l'acheter comme un livre électronique quelque part.



2
votes

J'ai aussi frappé d'alignement de la mémoire. La première chose à comprendre est que chaque objet a ses propres exigences d'alignement de la mémoire, qui est (au plus) la taille de l'objet lui-même. Heureusement, il est souvent plus petit.

Il existe des installations de deux types pour aider à écrire des allocateurs de mémoire corrects (C ++ 0x, ils peuvent être trouvés dans STD :: TR1 Dans la plupart des STL):

  • std :: alignment_of donne l'alignement comme une valeur de compilation
  • std :: aligné_storage donne un stockage aligné en fonction de ses paramètres

    Travailler avec les deux d'entre eux, vous obtiendrez ce dont vous avez besoin.

    Cependant, votre conception est légèrement hors base, pas à peu près à l'esprit, car votre en-tête n'aura pas la même exigence d'alignement (en général) que les objets stockés. xxx

    deux notes:

    • block peut avoir un alignement différent, mais peu importe
    • Vous pouvez utiliser Tailleof pour l'alignement, il est garanti de fonctionner, même si un peu gaspillé

      Enfin, vous pouvez également réussir sans toutes ces personnes, et un pointeur arithmétique, mais depuis sa proposition ...


3 commentaires

Merci pour la réponse, celles-ci ressemblent à elles seront utiles. Bien que je pense que je puisse passer dans l'alignement plutôt que je voudrais garder la piscine non spécifique de type; Mais cela signifie seulement que je vais utiliser Alignment_of ailleurs.


Les choses se compliquent un peu plus si vous allez la route générique, à cause de la fragmentation. N'hésitez pas à créer un pool de premier type (ou au moins spécifique d'alignement) puis enveloppez-le avec un générique qui gère simplement une collection d'entre eux.


Matthieu, une bonne idée. Éviter la fragmentation était l'un des points d'avoir une piscine en première place depuis que je soupçonne que je disposerai d'une quantité assez massive d'allocations mineures.