Alignement de la mémoire sur les processeurs modernes?

Je vois souvent du code tel que celui-ci lorsque, par exemple, représente un gros bitmap en mémoire:

size_t width = 1280; size_t height = 800; size_t bytesPerPixel = 3; size_t bytewidth = ((width * bytesPerPixel) + 3) & ~3; /* Aligned to 4 bytes */ uint8_t *pixelData = malloc(bytewidth * height); 

(c’est-à-dire une bitmap allouée sous forme de bloc de mémoire contiguë ayant une bytewidth alignée sur un certain nombre d’octets, le plus souvent 4).

Un point sur l’image est alors donné via:

 pixelData + (bytewidth * y) + (bytesPerPixel * x) 

Cela m’amène à deux questions:

  1. L’alignement d’un tampon de ce type a-t-il un impact sur les performances des processeurs modernes? Devrais-je me préoccuper de l’alignement, ou le compilateur va-t-il s’en charger?
  2. Si cela a un impact, quelqu’un peut-il m’indiquer une ressource pour trouver l’alignement d’octet idéal pour différents processeurs?

Merci.

Cela dépend de nombreux facteurs. Si vous n’accédez aux données de pixels qu’un octet à la fois, l’alignement ne fera aucune différence la plupart du temps. Pour lire / écrire un octet de données, la plupart des processeurs ne se soucient absolument pas de savoir si cet octet se trouve sur une limite de 4 octets ou non.

Toutefois, si vous accédez aux données dans des unités supérieures à un octet (par exemple, en unités de 2 octets ou de 4 octets), vous verrez certainement des effets d’alignement. Pour certains processeurs (par exemple, de nombreux processeurs RISC), il est totalement interdit d’accéder à des données non alignées à certains niveaux: toute tentative de lecture d’un mot de 4 octets à partir d’une adresse non alignée sur 4 octets générera une exception d’access aux données (ou une exception de stockage de données). ) sur un PowerPC, par exemple.

Sur d’autres processeurs (x86, par exemple), l’access à des adresses non alignées est autorisé, mais cela entraîne souvent une pénalité de performance masquée. Les charges / mémoires en mémoire sont souvent implémentées en microcode, et le microcode détectera l’access non aligné. Normalement, le microcode récupère la quantité appropriée de 4 octets dans la mémoire, mais s’il n’est pas aligné, il doit extraire deux emplacements de 4 octets de la mémoire et reconstruire la quantité souhaitée de 4 octets à partir des octets appropriés des deux emplacements. La récupération de deux emplacements de mémoire est évidemment plus lente qu’un.

Cela ne concerne toutefois que les charges et les magasins simples. Certaines instructions, telles que celles des jeux d’instructions MMX ou SSE, nécessitent que leurs opérandes mémoire soient correctement alignés. Si vous essayez d’accéder à la mémoire non alignée à l’aide de ces instructions spéciales, vous verrez quelque chose comme une exception d’instruction illégale.

Pour résumer, je ne m’inquiéterais pas vraiment de l’alignement à moins d’écrire du code extrêmement performant (par exemple, en assemblage). Le compilateur vous aide beaucoup, par exemple en complétant les structures de telle sorte que les quantités de 4 octets soient alignées sur des limites de 4 octets et que, sur x86, la CPU vous aide également lorsqu’il s’agit d’access non alignés. Étant donné que les données de pixels que vous utilisez sont des quantités de 3 octets, vous aurez presque toujours des access sur un octet.

Si vous décidez que vous souhaitez plutôt accéder aux pixels dans des access singuliers à 4 octets (par opposition à des access à 3 octets), il serait préférable d’utiliser des pixels de 32 bits et d’avoir chaque pixel individuel aligné sur une limite de 4 octets. L’alignement de chaque ligne sur une limite de 4 octets, mais pas chaque pixel, aura peu ou pas d’effet.

En fonction de votre code, je suppose que cela est lié à la lecture du format de fichier bitmap Windows – les fichiers bitmap nécessitent que la longueur de chaque ligne de balayage soit un multiple de 4 octets. Par conséquent, la configuration de vos tampons de données de pixels avec cette propriété a la propriété suivante: vous pouvez simplement lire l’intégralité de l’image bitmap d’un seul coup dans votre mémoire tampon (vous devez bien entendu tenir compte du fait que les lignes de balayage sont stockées de bas en haut au lieu de haut en bas et que les données de pixels sont BGR au lieu de RVB). Ce n’est cependant pas vraiment un avantage – ce n’est pas beaucoup plus difficile à lire dans une image bitmap une ligne à la fois.

Oui, l’alignement a un impact sur les performances des processeurs modernes – disons x86 -. Généralement, les charges et les magasins de données se produisent sur des limites d’alignement naturelles; Si vous insérez une valeur 32 bits dans un registre, il sera plus rapide s’il est déjà aligné sur une limite de 32 bits. Si ce n’est pas le cas, le x86 “s’en occupera pour vous”, en ce sens que le processeur fera toujours le travail, mais qu’il faudra beaucoup plus de cycles pour le faire, car il y aura des querelles internes ” re-aligner “l’access.

Bien entendu, dans la plupart des cas, ces frais généraux sont sortingviaux. Les structures de données binarys sont souvent regroupées de manière non alignée pour le transport sur le réseau ou la persistance sur disque, et les avantages en taille du stockage condensé l’emportent sur les performances d’exploitation occasionnelles sur ces données.

Mais en particulier avec les grands tampons de données uniformes auxquels on accède de manière aléatoire et où les performances globales sont vraiment importantes, comme dans le tampon de pixels ci-dessus, le maintien des structures de données alignées peut toujours être bénéfique.

Notez que dans le cas de l’exemple que vous donnez ci-dessus, seule chaque “ligne” de données de pixels est alignée. Les pixels eux-mêmes ont encore une longueur de 3 octets et sont souvent non alignés dans les “lignes”. Il n’y a donc pas grand avantage ici. Il existe des formats de texture, par exemple, qui ont 3 octets de données réelles par pixel et gaspillent littéralement un octet supplémentaire sur chacun d’eux pour maintenir les données alignées.

Il y a quelques informations plus générales ici: http://en.wikipedia.org/wiki/Data_structure_alignment

(Les caractéristiques spécifiques varient selon les architectures, à la fois en ce qui concerne les alignements naturels, que le processeur gère automatiquement les charges / magasins non alignés et que leur coût finisse par être élevé. Dans les cas où le processeur ne gère pas l’access de manière magique, souvent, le compilateur / C runtime fera tout ce qui est en son pouvoir pour faire ce travail pour vous.)

L’alignement de la mémoire tampon a un impact. La question est: est-ce un impact significatif? La réponse peut être très spécifique à l’application . Dans les architectures qui ne supportent pas nativement les access non alignés, par exemple, les modèles 68000 et 68010 (le 68020 ajoute un access non aligné), il s’agit véritablement d’un problème de performances et / ou de maintenance car le processeur sera défaillant, voire bloqué par un gestionnaire pour effectuer un access non aligné. .

L’alignement idéal pour différents processeurs peut être estimé: un alignement sur 4 octets convient aux architectures avec un chemin de données 32 bits. Alignement sur 8 octets pour 64 bits. Cependant, la mise en cache L1 a un effet . Pour de nombreux processeurs, il s’agit de 64 octets, même si cela changera sans doute à l’avenir.

Un alignement trop élevé (c’est-à-dire huit octets où il ne faut que deux octets) ne provoque aucune perte de performances pour les systèmes plus étroits, même sur un microcontrôleur 8 bits. Cela gaspille simplement (potentiellement) quelques octets de stockage.

Votre exemple est assez particulier: les éléments de 3 octets ont 50% de chances d’être individuellement non alignés (sur 32 bits), l’alignement de la mémoire tampon semble donc inutile, du moins pour des raisons de performances. Cependant, dans le cas d’un transfert en masse de l’ensemble, cela optimise le premier access. Notez qu’un premier octet non aligné peut également avoir un impact sur les performances lors du transfert vers un contrôleur vidéo.

  • L’alignement d’un tampon de ce type a-t-il un impact sur les performances des processeurs modernes?

Oui. Par exemple, si memcpy est optimisé à l’aide d’instructions SIMD (comme MMX / SSE), certaines opérations seront plus rapides avec une mémoire alignée. Dans certaines architectures, certaines instructions (du processeur) échouent si les données ne sont pas alignées. Il est donc possible que quelque chose fonctionne sur votre machine, mais pas sur une autre.

Avec des données alignées, vous exploitez également mieux les caches de la CPU.

  • Devrais-je me préoccuper de l’alignement, ou le compilateur va-t-il s’en charger?

Je devrais m’inquiéter de l’alignement lorsque j’utilise de la mémoire dynamic et que le compilateur ne peut pas le gérer (voir la réponse à ce commentaire).

Pour les autres éléments de votre code, vous avez le drapeau -malign et l’atsortingbut aligné avec lequel jouer.