web-dev-qa-db-fra.com

Quelle est la différence entre la taille de la taille et l'alignement?

Quelle est la différence entre la taille de la taille et l'alignement?

#include <iostream>

#define SIZEOF_ALIGNOF(T) std::cout<< sizeof(T) << '/' << alignof(T) << std::endl

int main(int, char**)
{
        SIZEOF_ALIGNOF(unsigned char);
        SIZEOF_ALIGNOF(char);
        SIZEOF_ALIGNOF(unsigned short int);
        SIZEOF_ALIGNOF(short int);
        SIZEOF_ALIGNOF(unsigned int);
        SIZEOF_ALIGNOF(int);
        SIZEOF_ALIGNOF(float);
        SIZEOF_ALIGNOF(unsigned long int);
        SIZEOF_ALIGNOF(long int);
        SIZEOF_ALIGNOF(unsigned long long int);
        SIZEOF_ALIGNOF(long long int);
        SIZEOF_ALIGNOF(double);
}

va sortir

1/1 1/1 2/2 2/2 4/4 4/4 4/4 4/4 4/4 8/8 8/8 8/8

Je pense que je ne comprends pas ce que l'alignement est ...?

45
user1494506

Eh bien, "mémoire" est essentiellement un vaste gamme d'octets. Cependant, la plupart des choses plus grandes comme les entiers ont besoin de plus de 1 octet pour les stocker - une valeur 32 bits, par exemple, utiliserait 4 octets de mémoire consécutifs.

Maintenant, les modules de mémoire de votre ordinateur ne sont généralement pas "octets"; Ils sont également organisés avec quelques octets "en parallèle", comme des blocs de 4 octets.

Pour une CPU, c'est beaucoup plus facile = plus efficace = meilleure performance pour ne pas "croiser" de telles bordures de blocs lors de la lecture de quelque chose comme un entier:

memory byte    0 1 2 3     4 5 6 7       8 9 10 11
 integer       goooood
                   baaaaaaaaad

C'est ce que dit "l'alignement": un alignement de 4 signifie que les données de ce type doivent (ou doivent, dépend de la CPU) doivent être stockées à partir d'une adresse d'un multiple de 4.

Vous observation que la taille de == Alignof est incorrecte; essayez des structures. Les structures seront également alignées (car leurs membres individuels doivent se retrouver sur les bonnes adresses), mais leur taille sera beaucoup plus grande.

70
Christian Stieber

Les deux opérateurs font fondamentalement différentes choses. sizeof donne la taille d'un type (combien de mémoire il prend) alors que alignof donne le nombre d'octets qu'un type doit être aligné. Il arrive que les primitives que vous avez testées ont une exigence d'alignement de la même manière que leur taille (ce qui a du sens si vous y réfléchissez).

Pensez à ce qui se passe si vous avez une structure à la place:

struct Foo {
     int a;
     float b;
     char c;
};

alignof(Foo) retournera 4.

17
tskuzzy

Ancienne question (bien que non marquée comme répondu ..) Mais pensait que cet exemple fait la différence un peu plus explicite en plus de la réponse de Christian Stieber. La réponse de Meluha contient également une erreur comme sizeof (S) de la sortie est 16 non 12.

// c has to occupy 8 bytes so that d (whose size is 8) starts on a 8 bytes boundary
//            | 8 bytes |  | 8 bytes  |    | 8 bytes |
struct Bad  {   char c;      double d;       int i;     }; 
cout << alignof(Bad) << " " << sizeof(Bad) << endl;   // 8 24

//             | 8 bytes |   |   8 bytes    |    
struct Good {   double d;     int i; char c;          };
cout << alignof(Good) << " " << sizeof(Good) << endl; // 8 16

Il démontre également qu'il est le mieux commandant des membres de la taille avec le plus grand premier (double dans ce cas), car les autres membres sont contraints par ce membre.

12
eolso

Pour les réponses fournies, il semble y avoir une certaine confusion sur ce qui alignement ​​en fait. La confusion se pose probablement parce qu'il y a 2 types d'alignement.

1. Alignement des membres

Il s'agit d'une mesure qualitative qui précise à quel point une instance est importante en nombre d'octets pour un ordre spécifique des membres dans la structure/type de classe. En règle générale, les compilateurs peuvent compacter des instances de structure/de classe si les membres sont commandés par leur octet-taille en ordre décroissant (c'est-à-dire les plus grands membres les plus petits derniers) dans la structure. Envisager:

struct A
{
  char c; float f; short s;
};

struct B
{
  float f; short s; char c;
};

Les deux structures contiennent exactement les mêmes informations. Pour l'amour de cet exemple; Le type de flotteur prend 4 octets, le type court prend 2 et le personnage prend 1 octet. Toutefois, la première structure A a des membres dans un ordre aléatoire, tandis que la deuxième structure B ordonne des membres selon leur taille d'octet (cela peut être différent sur certaines architectures, je suppose que x86 architecture de CPU Intel avec alignement de 4 octets dans cet exemple). Envisagez maintenant de la taille des structures:

printf("size of A: %d", sizeof (A)); // size of A: 12;
printf("size of B: %d", sizeof (B)); // size of B: 8;

Si vous vous attendez à ce que la taille soit 7 octets, vous supposeriez que les membres sont Emballé dans la structure à l'aide d'un alignement de 1 octet. Bien que certains compilateurs lui permettent, en général, la plupart des compilateurs utilisent 4 octets ou même des alignements de 8 octets en raison de raisons historiques (la plupart des travaux de la CPU avec des registres à usage général DWORD (double mot) ou QWOR (quad-word)).

Il y a 2 mécanismes de rembourrage au travail pour atteindre l'emballage.

  1. Premièrement, chaque membre qui a une taille d'octet inférieure à celle de l'alignement des octets est "fusionné" avec le (s) membre (s) suivant (s) si la taille d'octet résultant est inférieure ou égale à l'alignement des octets. Dans la structure B, les membres S et C peuvent être fusionnés de cette manière; Leur taille combinée est de 2 octets pour s + 1 octet pour C == 3 octets <= alignement de 4 octets. Pour la structure A, aucune fusion de ce type ne peut se produire, et chaque membre consomme efficacement 4 octets dans l'emballage de la structure.

  2. La taille totale de la structure est à nouveau rembourrée de sorte que la structure suivante puisse commencer à la limite d'alignement. Dans l'exemple B, le nombre total d'octets serait 7. La limite de 4 octets suivante réside dans l'octet 8, par conséquent, la structure est rembourrée avec 1 octet pour permettre aux allocations de matrices comme une séquence d'instances serrées.

Notez que Visual C++/GCC permet d'aligner différents alignements de 1 octet, 2 et des multiples plus élevés de 2 octets. Comprenez que cela fonctionne contre la capacité de votre compilateur à produire du code optimal pour votre architecture. En effet, dans l'exemple suivant, chaque octet serait lu comme un octet unique à l'aide d'une instruction mono-octet pour chaque opération de lecture. En pratique, le matériel entraînerait toujours la ligne de mémoire complète contenant chaque octet lu dans le cache et exécuter l'instruction 4 fois, même si les 4 octets sont assis dans le même Dword et pouvaient être chargés dans le registre de la CPU dans 1 instruction.

#pragma pack(Push,1)
struct Bad
{
  char a,b,c,d;
};
#pragma pack(pop)

2. Alignement de l'allocation

Ceci est étroitement lié au 2e mécanisme de remplissage expliqué dans la section précédente, cependant, Alignement d'allocation peut être spécifié dans des variantes de MALLOC/MEMALLOC fonctions d'allocation, par exemple. std :: aligné_alloc (). Par conséquent, il est possible d'attribuer un objet à un autre (multiple multiple multiple de 2) limite d'alignement que la structure/l'alignement des octets de type d'objet suggère.

size_t blockAlignment = 4*1024;  // 4K page block alignment
void* block = std::aligned_alloc(blockAlignment, sizeof(T) * count);

Le code placera le bloc de comptage des instances de type T sur des adresses terminées sur des multiples de 4096.

La raison de l'utilisation de tels alignements d'allocation est à nouveau purement architecturale. Par exemple, les blocs de lecture et d'écriture des adresses alignées de la page sont plus rapides car la gamme d'adresses correspond bien aux couches de cache. Les gammes qui sont divisées sur différentes "pages" de la corbeille au cache lors de la traversée de la limite de la page. Différents médias (architectures de bus) ont différents modèles d'accès et peuvent bénéficier de différents alignements. Généralement, les alignements de tailles de page 4, 16, 32 et 64 K ne sont pas rares.

Notez que la version et la plate-forme de langue fourniront généralement une variante spécifique de telles fonctions d'allocation alignées. EG.G La fonction POSIX_MEMALign compatible UNIX/Linux renvoie la mémoire par PTR argument et renvoie des valeurs d'erreur non nulle en cas de défaillance.

  • int POSIX_MEMALIGN (NOID ** MEMPTR, Taille_T Alignement, Taille_T Taille); // posix (linux/ux)
  • vide * aligné_alloc (taille_t alignement, taille_t taille); // c ++ 11
  • vide * std :: aligned_alloc (taille_t alignement, taille_t taille); // c ++ 17
  • vide * aligné_malloc (taille de taille_t, taille_t alignement); Microsoftvs2019
12
StarShine

La valeur d'alignement est la même que la valeur de taille de type de base.

La différence réside dans les types de données définis utilisés tels que l'utilisation de la structure; Pour un par exemple.

typedef struct { int a; double b; } S;
//cout<<alignof(s);                              outputp: 8;
//cout<<sizeof(S);                               output: 12;

par conséquent, la taille de la valeur est la taille totale requise pour le type de données donné; et l'alignement de la valeur est l'exigence d'alignement du plus grand élément de la structure.

Utilisation d'AlignoF: allouer la mémoire sur une limite d'alignement particulier.

5
Meluha

L'opérateur sizeof vous donne la taille en octets d'un type ou d'une instance d'un type.

L'opérateur alignof vous donne l'alignement des octets requis pour toute instance du type donné.

2
Man of One Way

Quelle est la différence entre sizeof et alignof?

Les deux sont des opérateurs. Les deux renvoient un type de size_t.

sizeof est la taille dans "octets" de l'objet - l'espace mémoire nécessaire pour le coder.

alignof est l'exigence d'alignement de l'adresse dans "octets" de l'objet. Une valeur de 1 implique aucune restriction d'alignement. 2 implique que l'adresse doit être une adresse même. 4 implique que l'adresse doit être une adresse quadruple. etc.

Lorsqu'une référence d'objet est tentée qui ne répond pas à l'exigence d'alignement, le résultat est comportement non défini.
Exemples:
. L'accès peut fonctionner, seulement plus lent.
. La tentative d'accès peut tuer le programme.

// Assume alignof(int) --> 2
char a[4];   // It is not known that `a` begins on an odd or even address
int *p = a;  // conversion may fail
int d = *p;  // *p is UB.

Exemple d'extension et de sortie du code de l'OP.

  SIZEOF_ALIGNOF(double);
  SIZEOF_ALIGNOF(complex double);
  SIZEOF_ALIGNOF(div_t);
  SIZEOF_ALIGNOF(max_align_t);

8/8
16/8
8/4
32/16