GNT sans publicité, site mobile, fonctionnalitées exclusives...

Alignement de char

Le
Randolf Carter
Salut,

J'aimerai reprendre sur des bases saines la discussion sur
l'alignement des chars qui est un peu partie en eau de boudin. En
guise d'illustration voici, par exemple, trois définitions de type :

1)
typedef struct
{
char c1, c2, c3, c4;
} T1;

2)
struct MyStruct
{
char c;
};
typedef struct MyStruct T2[4];

3)
typedef char T3[4];

Soyons d'accord dès le départ, la norme dit *très clairement* que T1,
T2 et T3 peuvent avoir chacun des représentations mémoires
parfaitement distinctes. En effet il peut y avoir du padding entre les
char de T1, et même dans la struct MyStruct de T2. Il est donc
parfaitement interdit de caster un T1* en T2*, un T2* en T3*, etc. La
question n'est pas là.

Maintenant que je sais que sizeof(char) est égal à 1 par définition
(merci encore pour cette précision !), il est aussi clair que
sizeof(T3) est toujours égal à 4. Mes char sont donc bien rangés les
uns derrière les autres, et mon système n'a aucun problème pour
lire/écrire chacun d'eux. Je veux dire par là qu'il n'y a donc aucune
contrainte technique (hardware ou autre) qui pourrait empêcher mes
char d'être rangés comme cela en mémoire.

Ma question est d'ordre pratique et ne concerne pas la norme. Par
simple curiosité, est-ce que vous pourriez me donner toutes les
raisons possibles qui pourraient vous passer la tête pour que T1 ou T2
n'ait pas la même représentation en mémoire que T3 ? Qu'est ce qui
pourrait pousser un compilateur à rajouter du padding dans T1 ou dans
MyStruct ?
La première qui me vient à l'esprit est la performance. En effet, il
pourrait être plus efficace (mais pas obligatoire) sur certain système
que les char soient alignés sur des mots par exemple.

Notez que ma question reste valide si on remplace le type 'char' par
n'importe quel autre type dans mes trois définitions ci dessus.

Merci pour vos réponses éclairées.

--
- Randolf
Lire les 8 réponses

Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Vincent Lefevre
Le #550761
Dans l'article Randolf Carter
Ma question est d'ordre pratique et ne concerne pas la norme. Par
simple curiosité, est-ce que vous pourriez me donner toutes les
raisons possibles qui pourraient vous passer la tête pour que T1 ou T2
n'ait pas la même représentation en mémoire que T3 ? Qu'est ce qui
pourrait pousser un compilateur à rajouter du padding dans T1 ou dans
MyStruct ?


Parce que le compilateur est un peu bête et qu'il peut y avoir du
padding pour d'autres types?

La première qui me vient à l'esprit est la performance. En effet, il
pourrait être plus efficace (mais pas obligatoire) sur certain système
que les char soient alignés sur des mots par exemple.


Ça peut rendre dans certains cas l'adresse calculable avec moins
d'instructions (sur ARM par exemple).

--
Vincent Lefèvre validated (X)HTML - Acorn Risc PC, Yellow Pig 17, Championnat International
des Jeux Mathématiques et Logiques, TETRHEX, etc.
Work: CR INRIA - computer arithmetic / SPACES project at LORIA

Randolf Carter
Le #550756
Parce que le compilateur est un peu bête et qu'il peut y avoir du
padding pour d'autres types?


Pas très sympa pour les compilateurs, mais effectivement rien ne les
empêche d'en rajouter même si c'est pour rien. Cependant je n'en ai
jamais encore rencontré un. J'oserai même appeler ça un bug car il y
aurait une consommation mémoire accrue non justifiée.


Ça peut rendre dans certains cas l'adresse calculable avec moins
d'instructions (sur ARM par exemple).


Bon point. Tu peux juste détailler un peu plus ou donner un lien qui
explique pourquoi ?
Merci.

--
- Randolf

Marc Boyer
Le #550539
Randolf Carter wrote:
Ma question est d'ordre pratique et ne concerne pas la norme. Par
simple curiosité, est-ce que vous pourriez me donner toutes les
raisons possibles qui pourraient vous passer la tête pour que T1 ou T2
n'ait pas la même représentation en mémoire que T3 ? Qu'est ce qui
pourrait pousser un compilateur à rajouter du padding dans T1 ou dans
MyStruct ?


Avec les exemples que tu donnes, je vois pas, mais imaginons
cela, sur un proc 32 bits.
typedef struct pad {
char c3[3];
char c4[4];
int i;
} pad;

A cause du "int i", le compilo sait qu'il devra
mettre un bit de padding quelque part. Autant le mettre
entre c3 et c4.
Même si on imagine qu'on a pas de i, un bit de padding
offre un bon compromis global entre surcharge mémoire
(12%) et amélioration des accès (lire tout c3 ou tout
c4 se fait en un seul accès, en supposant des lectures
mémoires 32 bis).

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Vincent Lefevre
Le #550537
Dans l'article Randolf Carter
Ça peut rendre dans certains cas l'adresse calculable avec moins
d'instructions (sur ARM par exemple).


Bon point. Tu peux juste détailler un peu plus ou donner un lien qui
explique pourquoi ?


Dans un calcul d'adresse, il y a souvent une constante à ajouter
(offset) par rapport à une adresse de base, l'adresse de base étant
alignée sur un multiple de 4 (ou une plus grande puissance de 2).
En gros, sur ARM, une constante peut s'exprimer dans une instruction
assembleur de la façon suivante: n << c, où n est un entier sur un
nombre limité de bits, et c un décalage. Si l'offset est un multiple
de 4, ceci signifie qu'on gagne deux bits sur n. Le fait d'ajouter
des octets de padding peut donc rendre certaines adresses calculables
en une instruction. Mais c'est sûr qu'il ne faut pas le faire à
chaque fois (sinon on risque de ne rien y gagner et de perdre beaucoup
en mémoire) et qu'il faut un minimum d'intelligence derrière.

--
Vincent Lefèvre validated (X)HTML - Acorn Risc PC, Yellow Pig 17, Championnat International
des Jeux Mathématiques et Logiques, TETRHEX, etc.
Work: CR INRIA - computer arithmetic / SPACES project at LORIA


kilobug
Le #550534

Randolf Carter wrote:
Ma question est d'ordre pratique et ne concerne pas la norme. Par
simple curiosité, est-ce que vous pourriez me donner toutes les
raisons possibles qui pourraient vous passer la tête pour que T1 ou T2
n'ait pas la même représentation en mémoire que T3 ? Qu'est ce qui
pourrait pousser un compilateur à rajouter du padding dans T1 ou dans
MyStruct ?



Avec les exemples que tu donnes, je vois pas, mais imaginons
cela, sur un proc 32 bits.
typedef struct pad {
char c3[3];
char c4[4];
int i;
} pad;


A cause du "int i", le compilo sait qu'il devra
mettre un bit de padding quelque part. Autant le mettre
entre c3 et c4.
Même si on imagine qu'on a pas de i, un bit de padding
offre un bon compromis global entre surcharge mémoire
(12%) et amélioration des accès (lire tout c3 ou tout
c4 se fait en un seul accès, en supposant des lectures
mémoires 32 bis).


Un octet, pas un bit ;)

Mais sinon, ce qui est important c'est de comprendre qu'un accès
non-aligné est soit lent (sur une architecture qui le supporte) soit
complètement interdit (sur beaucoup d'architectures RISC, comme le
StrongARM, ...)

Un accès non-aligné est lorsque une donné chevauche deux zones d'une
taille fixée. Sur un processeur 32-bits, les zones font en général
32-bits. Si on veut charger 4 otects à l'addresse 0x420 il n'y a pas
de problème, 0x420 est divisble par 4, et le processeur peut charger
les 4 octets d'un coup.

Mais, si on veut charger quatre octets à 0x421, là il y a problème. Le
bus mémoire ne peut pas transférer dans la même opération les octets
de la zone 0x420-0x423 et ceux de la zone 0x424-0x427. Donc, soit le
processeur l'émule en faisant deux accès mémoire, un pour les trois
premiers octets, un pour le dernier; soit il ne l'émule pas, et lève
donc une exception. Certaisn OS peuvent émuler à la main les deux
accès, mais le coût en performance est alors immense (exception,
entrée en mode noyau, décodage de l'instruction par le noyau,
émulation software, pouis retour en mode utilisateur), la plupart des
OS se contentent de terminer le programme en considérantr que l'accès
mémoire est invalide (sous Unix, le signal SIGBUS est utilisé).

Voilà pourquoi les compilos rajoutent des octets de padding afin que
les accès mémoires soit rapides et possibles partout. Il faut aussi
avoir ça à l'esprit lorsque l'on fait un cast:

char plop[420];
int plip = 3;

*((int *)(plop + plip)) = 0;

ce code là ne fonctionnera pas sur un StrongARM.

gcc peut l'indiquer:
(, 6) ~ $ arm-linux-gcc -Wcast-align -Wall plop.c -o plop
plop.c: In function `main':
plop.c:10: warning: cast increases required alignment of target type
(, 7) ~ $ gcc -Wcast-align -Wall plop.c -o plop
(, 8) ~ $

Le cross-compilateur vers StrongARM me prévient qu'il y a un problème,
le compilateur natif vers ia32 ne dit rien car l'ia32 supporte les
accès non alignés.

--
Gael Le Mignot "Kilobug" - - http://kilobug.free.fr
GSM : 06.71.47.18.22 (in France) ICQ UIN : 7299959
Fingerprint : 1F2C 9804 7505 79DF 95E6 7323 B66B F67B 7103 C5DA

Member of HurdFr: http://hurdfr.org - The GNU Hurd: http://hurd.gnu.org


Publicité
Suivre les réponses
Poster une réponse
Anonyme