« Fonctionnement d'un ordinateur/Le modèle mémoire : alignement et boutisme » : différence entre les versions

Contenu supprimé Contenu ajouté
Ligne 26 :
 
Dans la réalité, ces blocs ont une taille égale à une puissance de deux : cela permet de faire quelques bidouilles sur le bus d'adresse pour économiser des fils. Si la taille d'un mot est égale à <math>2^{n}</math>, seules les adresses multiples de <math>2^{n}</math> seront utilisables. Hors, ces adresses se reconnaissent facilement : leurs n bits de poids faibles valent zéro. On n'a donc pas besoin de câbler les fils correspondant à ces bits de poids faible et on peut se contenter de les connecter à la masse (le zéro volt vu dans le second chapitre).
 
====Accès mémoire non-alignés====
 
Maintenant imaginons un cas particulier : je dispose d'un processeur utilisant des mots de 4 octets. Je dispose aussi d'un programme qui doit manipuler un caractère stocké sur 1 octet, un entier de 4 octets et une donnée de deux octets. Mais un problème se pose : le programme qui manipule ces données a été programmé par quelqu'un qui n'était pas au courant de ces histoire d'alignement, et il a répartit mes données un peu n'importe comment. Supposons que cet entier soit stocké à une adresse non-multiple de 4. Par exemple :
 
{|class=wikitable
|-
!Adresse
!Octet 4
!Octet 3
!Octet 2
!Octet 1
|-
|0x 0000 0000
|Caractère
|Entier
|Entier
|Entier
|-
|0x 0000 0004
|Entier
|Donnée
|Donnée
|
|0x 0000 0008
|
|
|
|
|}
 
Pour charger mon caractère dans un registre, pas de problèmes : celui-ci tient dans un mot. Il me suffit alors de charger mon mot dans un registre en utilisant une instruction de mon processeur qui charge un octet. Pour ma donnée de 2 octets, pas de problèmes non plus ! Le processeur charge le mot entier en ne sélectionne que les octets utiles, chose possible avec quelques décalage et un masque. Mais les problèmes arrivent quand il s'agit de charger l'entier. L'entier est en effet stocké sur deux mots différents, et on ne peut le charger en une seule fois : on dit que l'entier n'est pas aligné en mémoire.
 
Dans ce cas, il peut se passer des tas de choses suivant le processeur. Sur certains processeurs, la donnée est chargée en deux fois : c'est légèrement plus lent que la charger en une seule fois, mais ça passe. Mais sur d'autres processeurs, la situation devient nettement plus grave : le processeur ne peut en effet gérer ce genre d'accès mémoire dans ses circuits et considère qu'il est face à une erreur, similaire à une division par zéro ou quelque chose dans le genre. Il va alors interrompre le programme en cours d’exécution et exécuter un petit sous-programme qui gérera cette erreur. On dit que notre processeur effectue une exception matérielle. Si on est chanceux, ce programme de gestion d'erreur chargera cette donnée en deux fois : ça prend beaucoup de temps. Mais sur d'autres processeurs, le programme responsable de cet accès mémoire en dehors des clous se fait sauvagement planter. Par exemple, essayez de manipuler une donnée qui n'est pas "alignée" dans un mot de 16 octets avec une instruction SSE, vous aurez droit à un joli petit crash ! C'est pas pour rien que ce genre d'instructions est si peu utilisé par nos compilateurs. :p
 
Pour éviter ce genre de choses, les compilateurs utilisés pour des langages de haut niveau préfèrent rajouter des données inutiles (on dit aussi du padding) de façon à ce que chaque donnée soit bien alignée sur le bon nombre d'octets. En reprenant notre exemple du dessus, et en notant le padding X, on obtiendrait ceci :
 
{|class=wikitable
|-
!Adresse
!Octet 4
!Octet 3
!Octet 2
!Octet 1
|-
|0x 0000 0000
|Caractère
|X
|X
|X
|-
|0x 0000 0004
|Entier
|Entier
|Entier
|Entier
|0x 0000 0008
|Donnée
|Donnée
|X
|X
|}
 
Comme vous le voyez, ça prend un peu plus de place, et de la mémoire est gâchée inutilement. C'est pas grand chose, mais quand on sait que de la mémoire cache est gâchée ainsi, ça peut jouer un peu sur les performances. Il y a aussi des situations dans lesquelles rajouter du padding est une bonne chose et permet des gains en performances assez abominables : une sombre histoire de cache dans les architectures multiprocesseurs ou multicores, mais je n'en dit pas plus. Moralité : programmeurs, faites gaffe à bien gérer l'alignement en mémoire !
 
Cet alignement se gère dans certains langages (comme le C, le C++ ou l'ADA), en gérant l'ordre de déclaration de vos variables. Essayez toujours de déclarer vos variables de façon à remplir un mot intégralement ou le plus possible. Renseignez-vous sur le padding, et essayez de savoir quelle est la taille de vos données en regardant la norme de vos langages.
 
==Boutisme==