Partager via


align (C++)

Dans Visual Studio 2015 et versions ultérieures, utilisez le spécificateur alignas (C++11) pour contrôler l’alignement. Pour plus d’informations, consultez Alignement.

Section spécifique à Microsoft

Utilisez __declspec(align(#)) pour contrôler avec précision l'alignement des données définies par l'utilisateur (par exemple, les allocations statiques ou les données automatiques dans une fonction).

Syntaxe

__declspec( align( # ) ) declarator

Notes

L'écriture d'applications qui utilisent les dernières instructions du processeur introduit de nouvelles contraintes et de nouveaux problèmes. Un grand nombre de nouvelles instructions nécessitent que les données soient alignées sur des limites de 16 octets. En alignant les données fréquemment utilisées sur la taille de ligne de cache du processeur, vous améliorez les performances du cache. Par exemple, si vous définissez une structure dont la taille est inférieure à 32 octets, vous voudrez peut-être un alignement sur 32 octets pour garantir que les objets de ce type de structure sont mis en cache efficacement.

# est la valeur d’alignement. Les entrées valides sont des puissances entières de deux comprises entre 1 et 8192 (octets), telles que 2, 4, 8, 16, 32 ou 64. declarator correspond aux données que vous déclarez comme alignées.

Pour plus d’informations sur la façon de retourner une valeur de type size_t qui représente l’alignement requis du type, consultez alignof. Pour plus d’informations sur la façon de déclarer des pointeurs non alignés pendant le ciblage des processeurs 64 bits, consultez __unaligned.

Vous pouvez utiliser __declspec(align(#)) lorsque vous définissez un struct, une union ou une class, ou lorsque vous déclarez une variable.

Le compilateur ne garantit pas, ni ne tente de conserver, l’attribut d’alignement des données durant une opération de copie ou de transformation de données. Par exemple, memcpy peut copier un struct déclaré avec __declspec(align(#)) à n’importe quel endroit. Les allocateurs ordinaires (par exemple, les allocateurs malloc, C++ operator new et Win32) retournent généralement la mémoire qui n’est pas alignée pour les structures ou tableaux de structures __declspec(align(#)). Pour garantir l’alignement approprié de la destination d’une opération de copie ou de transformation de données, utilisez _aligned_malloc. Ou écrivez votre propre allocateur.

Vous ne pouvez pas spécifier d’alignement pour les paramètres de fonction. Quand vous passez des données qui ont un attribut d’alignement par valeur dans la pile, la convention d’appel contrôle son alignement. Si l'alignement des données est important dans la fonction appelée, copiez le paramètre dans la mémoire correctement alignée avant de l'utiliser.

Sans __declspec(align(#)), le compilateur aligne généralement les données sur les limites naturelles en fonction du processeur cible et de la taille des données, par exemple des limites de 4 octets sur les processeurs 32 bits et des limites de 8 octets sur les processeurs 64 bits. Les données dans les classes ou les structures sont alignées dans la classe ou la structure au minimum de leur alignement naturel et du paramètre de compression actuel (à partir du #pragma pack ou de l’option du compilateur /Zp).

Cet exemple illustre l'utilisation de __declspec(align(#)) :

__declspec(align(32)) struct Str1{
   int a, b, c, d, e;
};

Ce type a maintenant un attribut d'alignement de 32 octets. Cela signifie que toutes les instances statiques et automatiques commencent à une limite de 32 octets. D’autres types de structure déclarés avec ce type en tant que membre conservent l’attribut d’alignement de ce type. Autrement dit, toute structure avec Str1 en tant qu’élément a un attribut d’alignement d’au moins 32.

Ici, sizeof(struct Str1) est égal à 32. Cela implique que si un tableau d’objets Str1 est créé et si la base du tableau est alignée sur 32 octets, chaque membre du tableau est également aligné sur 32 octets. Pour créer un tableau dont la base est correctement alignée en mémoire dynamique, utilisez _aligned_malloc. Ou écrivez votre propre allocateur.

La valeur sizeof d'une structure quelconque est le décalage du membre final, plus la taille de ce membre, arrondie au multiple le plus proche de la plus grande valeur membre d'alignement du membre ou de la valeur d'alignement de la structure entière, quelle que soit la valeur la plus grande.

Le compilateur utilise ces règles pour l'alignement de la structure :

  • À moins d'une substitution par __declspec(align(#)), l'alignement d'un membre de structure scalaire représente le minimum de sa taille et de la compression actuelle.

  • À moins d'une substitution par __declspec(align(#)), l'alignement d'une structure est la valeur maximale des alignements de ses membres.

  • Un membre de structure est placé dans un décalage par rapport au début de sa structure parente qui est le plus petit multiple de son alignement supérieur ou égal au décalage de la fin du membre précédent.

  • La taille d'une structure est le plus petit multiple de son plus grand alignement supérieur ou égal au décalage de la fin de son dernier membre.

__declspec(align(#)) peut uniquement augmenter les restrictions d'alignement.

Pour plus d’informations, consultez l’article suivant :

align Exemples

Les exemples suivants montrent comment __declspec(align(#)) affecte la taille et l'alignement des structures de données. Les exemples supposent les définitions suivantes :

#define CACHE_LINE  32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))

Dans cet exemple, la structure S1 est définie à l'aide de __declspec(align(32)). Toutes les utilisations de S1 pour une définition de variable ou dans d'autres types de déclaration sont alignées sur 32 octets. sizeof(struct S1) retourne 32, et S1 a 16 octets de remplissage après les 16 octets requis pour contenir les quatre entiers. Chaque membre int a besoin d'un alignement sur 4 octets, mais l’alignement de la structure elle-même est déclaré sur 32 octets. Donc, l’alignement global est 32 octets.

struct CACHE_ALIGN S1 { // cache align all instances of S1
   int a, b, c, d;
};
struct S1 s1;   // s1 is 32-byte cache aligned

Dans cet exemple, sizeof(struct S2) retourne 16, qui est exactement la somme des tailles membres, car il s’agit d’un multiple de la plus grande exigence d’alignement (un multiple de 8).

__declspec(align(8)) struct S2 {
   int a, b, c, d;
};

Dans l'exemple suivant, sizeof(struct S3) retourne 64.

struct S3 {
   struct S1 s1;   // S3 inherits cache alignment requirement
                  // from S1 declaration
   int a;         // a is now cache aligned because of s1
                  // 28 bytes of trailing padding
};

Dans cet exemple, notez que a a l'alignement de son type naturel, dans le cas présent, 4 octets. Toutefois, S1 doit être aligné sur 32 octets. 28 octets de remplissage suivent a, afin que s1 démarre au décalage 32. S4 hérite de la spécification d’alignement de S1 parce qu’il s’agit de la plus grande spécification d’alignement de la structure. sizeof(struct S4) retourne 64.

struct S4 {
   int a;
   // 28 bytes padding
   struct S1 s1;      // S4 inherits cache alignment requirement of S1
};

Les trois déclarations de variable suivantes utilisent également __declspec(align(#)). Dans chaque cas, la variable doit être alignée sur 32 octets. Dans le tableau, l’adresse de base du tableau, les membres du tableau ne sont pas tous alignés sur 32 octets. La valeur sizeof de chaque membre du tableau n’est pas affectée lorsque vous utilisez __declspec(align(#)).

CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;

Pour aligner chaque membre d'un tableau, écrivez un code tel que le suivant :

typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];

Dans cet exemple, notez que l'alignement de la structure elle-même et l'alignement du premier élément ont le même effet :

CACHE_ALIGN struct S6 {
   int a;
   int b;
};

struct S7 {
   CACHE_ALIGN int a;
               int b;
};

S6 et S7 ont les mêmes caractéristiques d'alignement, d'allocation et de taille.

Dans cet exemple, l’alignement des adresses de début de a, b, c et d sont 4, 1, 4 et 1, respectivement.

void fn() {
   int a;
   char b;
   long c;
   char d[10]
}

Quand la mémoire est allouée sur le tas, l'alignement dépend de la fonction d'allocation appelée. Par exemple, si vous utilisez malloc, le résultat dépend de la taille d’opérande. Si arg>= 8, la mémoire retournée est alignée sur 8 octets. Si arg< 8, l’alignement de la mémoire retournée représente la première puissance de 2 inférieure à arg. Par exemple, si vous utilisez malloc(7), l’alignement est 4 octets.

Définition de nouveaux types avec __declspec(align(#))

Vous pouvez définir un type avec une caractéristique d'alignement.

Par exemple, vous pouvez définir un struct avec une valeur d’alignement comme suit :

struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;

aType et bType sont maintenant de la même taille (8 octets), mais les variables de type bType sont alignées sur 32 octets.

Alignement des données dans le stockage local de thread

Le stockage local des threads de type statique (TLS) créé avec l'attribut __declspec(thread) et placé dans la section TLS de l'image contribue à l'alignement exactement comme les données statiques normales. Pour créer des données TLS, le système d'exploitation alloue de la mémoire de la taille de la section TLS et respecte l'attribut d'alignement de la section TLS.

Cet exemple montre différentes façons de placer des données alignées dans le stockage local des threads.

// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;

// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;

// create an aligned structure
struct CACHE_ALIGN S9 {
   int a;
   int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;

Comment align fonctionne avec la compression de données

L’option du compilateur /Zp et le pragma pack ont pour effet de compresser les données des membres de structure et d’union. Cet exemple montre comment /Zp et __declspec(align(#)) fonctionnent ensemble :

struct S {
   char a;
   short b;
   double c;
   CACHE_ALIGN double d;
   char e;
   double f;
};

Le tableau suivant répertorie le décalage de chaque membre pour différentes valeurs /Zp (ou #pragma pack), en indiquant leur mode d’interaction.

Variable /Zp1 /Zp2 /Zp4 /Zp8
a 0 0 0 0
b 1 2 2 2
c 3 4 4 8
j 32 32 32 32
e 40 40 40 40
f 41 42 44 48
sizeof(S) 64 64 64 64

Pour plus d’informations, consultez /Zp (Alignement des membres de la structure).

Le décalage d'un objet est basé sur le décalage de l'objet précédent et du paramètre de compactage actif, sauf si l'objet a un attribut __declspec(align(#)). Dans ce cas, l'alignement est basé sur le décalage de l'objet précédent et de la valeur __declspec(align(#)) pour l'objet.

FIN de la section spécifique à Microsoft

Voir aussi

__declspec
Vue d’ensemble des conventions ABI ARM
Conventions des logiciels x64