24 votes

Détermination de l'alignement maximal possible en C++

Est-ce qu'il y a portable manière de déterminer quel est l'alignement maximum possible pour tout type est ?

Par exemple, sur x86, les instructions SSE requièrent un alignement de 16 octets, mais pour autant que je sache, aucune instruction ne requiert plus que cela, de sorte que tout type peut être stocké en toute sécurité dans un tampon aligné sur 16 octets.

J'ai besoin de créer un tampon (tel qu'un tableau de chars) dans lequel je peux écrire des objets de types arbitraires, et je dois donc pouvoir compter sur le fait que le début du tampon est aligné.

Si tout le reste échoue, je sais que l'allocation d'un tableau de chars avec new est garanti pour avoir un alignement maximal, mais avec les modèles TR1/C++0x alignment_of y aligned_storage Je me demande s'il ne serait pas possible de créer le tampon sur place dans ma classe de tampon, plutôt que de nécessiter l'indirection de pointeur supplémentaire d'un tableau alloué dynamiquement.

Des idées ?

Je réalise qu'il existe de nombreuses options pour déterminer l'alignement maximum pour un ensemble limité de types : Une union, ou juste alignment_of de TR1, mais mon problème est que l'ensemble des types n'est pas limité. Je ne sais pas à l'avance quels objets doivent être stockés dans le tampon.

15voto

Ricky65 Points 538

En C++11 std::max_align_t défini dans l'en-tête cstddef est un type POD dont l'exigence d'alignement est au moins aussi stricte (aussi grande) que celle de tout type scalaire.

En utilisant le nouvel opérateur alignof, il serait aussi simple que alignof(std::max_align_t)

11voto

James McNellis Points 193607

En C++0x, l'élément Align paramètre de modèle de std::aligned_storage<Len, Align> a un argument par défaut de "default-alignment", qui est défini comme (N3225 §20.7.6.6 Table 56) :

La valeur de default-alignment doit être l'exigence d'alignement la plus stricte pour tout type d'objet C++ dont la taille n'est pas supérieure à Len .

Il n'est pas clair si les types SSE seraient considérés comme des "types d'objets C++".

L'argument par défaut ne faisait pas partie du TR1. aligned_storage ; il a été ajouté pour C++0x.

7voto

stinky472 Points 4864

Malheureusement, assurer un alignement maximal est beaucoup plus difficile qu'il ne devrait l'être, et il n'y a pas de solution garantie AFAIK. De la GotW blog ( Article de Pimpl rapide ) :

union max_align {
  short       dummy0;
  long        dummy1;
  double      dummy2;
  long double dummy3;
  void*       dummy4;
  /*...and pointers to functions, pointers to
       member functions, pointers to member data,
       pointers to classes, eye of newt, ...*/
};

union {
  max_align m;
  char x_[sizeofx];
};

Ce n'est pas garanti d'être entièrement portable, mais en pratique c'est proche car il y a peu ou pas de systèmes systèmes sur lesquels cela ne fonctionnera pas comme comme prévu.

C'est à peu près le "hack" le plus proche que je connaisse pour ça.

Il existe une autre approche que j'ai utilisée personnellement pour une allocation super rapide. Notez qu'elle est mauvaise, mais je travaille dans le domaine du raytracing où la vitesse est l'une des plus grandes mesures de la qualité et nous profilons le code quotidiennement. Il s'agit d'utiliser un allocateur de tas avec de la mémoire pré-allouée qui fonctionne comme la pile locale (il suffit d'incrémenter un pointeur lors de l'allocation et de le décrémenter lors de la désallocation).

Je l'utilise pour Pimpls particulièrement. Cependant, il ne suffit pas d'avoir l'allocateur ; pour qu'un tel allocateur fonctionne, nous devons supposer que la mémoire d'une classe, Foo, est allouée dans un constructeur, que la même mémoire est également désallouée uniquement dans le destructeur, et que Foo lui-même est créé sur la pile. Pour être sûr, j'avais besoin d'une fonction pour voir si le pointeur 'this' d'une classe se trouve sur la pile locale afin de déterminer si nous pouvons utiliser notre allocateur de pile super rapide basé sur le tas. Pour cela, nous avons dû rechercher des solutions spécifiques au système d'exploitation : J'ai utilisé TIBs y TEBs pour Win32/Win64, et mes collègues ont trouvé des solutions pour Linux et Mac OS X.

Le résultat, après une semaine de recherche de méthodes spécifiques au système d'exploitation pour détecter l'étendue de la pile, les exigences d'alignement, et après de nombreux tests et profilages, était un allocateur capable d'allouer de la mémoire en 4 cycles d'horloge selon nos benchmarks de compteur de tic, contre environ 400 cycles pour malloc/operator new (notre test impliquait une contention de threads, donc malloc est susceptible d'être un peu plus rapide que cela dans les cas de threads uniques, peut-être quelques centaines de cycles). Nous avons ajouté une pile de stockage par thread et détecté quel thread était utilisé, ce qui a augmenté le temps à environ 12 cycles, bien que le client puisse suivre l'allocateur du thread pour obtenir les allocations de 4 cycles. Cela a permis d'effacer de la carte les points chauds basés sur l'allocation de mémoire.

Bien que vous n'ayez pas besoin de vous donner tout ce mal, l'écriture d'un allocateur rapide pourrait être plus facile et plus généralement applicable (ex : permettre à la quantité de mémoire à allouer/désallouer d'être déterminée au moment de l'exécution) que quelque chose comme max_align aquí. max_align est assez facile à utiliser, mais si vous recherchez la vitesse pour les allocations de mémoire (et en supposant que vous avez déjà profilé votre code et trouvé des points chauds dans malloc/free/operator new/delete, les principaux contributeurs étant dans le code que vous contrôlez), écrire votre propre allocateur peut vraiment faire la différence.

5voto

David Seiler Points 6212

A défaut de maximally_aligned_t type que tous les compilateurs promettent fidèlement de supporter toutes les architectures partout, je ne vois pas comment cela pourrait être résolu au moment de la compilation. Comme vous le dites, l'ensemble des types potentiels est illimité. L'indirection supplémentaire des pointeurs est-elle vraiment si importante ?

1voto

Martin Beckett Points 60406

L'allocation de mémoire alignée est plus délicate qu'il n'y paraît - voir par exemple Mise en œuvre de l'allocation de mémoire alignée

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X