Taille d'un Boolean en memoire
Récemment Bidou, Coq et moi nous sommes posé la question suivante : "Comment un Boolean est stocké en mémoire en C# ?"
D'instinct on a pensé 1 bit puis 1 octet puis 2 puis 4, bref on ne savait pas exactement quelle était la bonne réponse. On a donc demandé à Mitsu Furuta (Relation technique Développeurs chez Microsoft France) et voici sa réponse :
Il convient de distinguer 2 choses : La taille mémoire et l’adressage.
Un booléen est en effet une valeur binaire qui n’a besoin que d’un bit pour stocker sa valeur. Cependant, un processeur 32bits n’est capable d’adresser des adresses physiques que tous les 32bits.
En fait il peut le faire à l’octet prêt mais ira d’abord à l’adresse 32bits la plus proche et fera un offset de 0, 8, 16 ou 24bits pour avoir l’octet (un masque puis un décalage).
Conclusion :
- C’est plus lent de lire un octet ou un double !
- Qu’on lise 8 ou 16bits, on en lit toujours 32 !
La réponse la plus correcte pour tout le monde est qu’un booléen ne consomme que 1 octet et c’est ce que renverra sizeof en mode unsafe.
Je vais pourtant vous montrer que l’adressage peut fausser cette information.
Testons ici la taille de StructTest.
struct StructTest
{
bool b;
}
sizeof renvoie 1
struct StructTest
{
bool b;
int i;
}
sizeof renvoie 8 !
La meilleur démo pour finir :
struct StructTest
{
bool b;
bool b2;
int i;
}
sizeof renvoie toujours 8 dans ce cas, car le compilateur a pris le choix d’aligner b et b2 dans le même 32bits.
La réponse la plus précise à mon sens est donc que la taille réellement consommée par une variable dans un programme dépend du contexte dans lequel elle est utilisée.
Allez, encore quelques astuces :
struct StructTest
{
bool b;
bool b2;
int i;
}
Consomme 8 octets. 1x32bits pour les deux bool et un int32.
struct StructTest
{
bool b;
int i;
bool b2;
}
Quasiment la même définition mais réordonnée : consomme 12 octets (3x32bits).
Les deux bool n’étant pas côte à côte, le compilateurs leur réserve chacun 32bits.
L’alignement par défaut de C# est séquentiel, c'est-à-dire qu’il respecte l’ordre d’écriture des champs.
On peut modifier cet alignement avec l’attribute StructLayout :
[StructLayout(LayoutKind.Auto)]
struct StructTest
{
bool b;
int i;
bool b2;
}
Auto optimise le regroupement de données dont la taille est inférieure à 32bits. Ainsi, les deux bools seront replacés côte à côte afin que la structure ne consomme que 8 octects. Il faut faire très attention à cette méthode lorsque l’on fait de l’intérop car l’ordre des champs n’est pas celui qu’on croit.
Enfin, [StructLayout(LayoutKind.Explicit)] vous permet de définir l’offset exact de chaque champ dans la structure (avec FieldOffset sur chaque champ).
Pour toutes ces raisons, si vous avez beaucoup de bits à manipuler, il est catastrophique d’utiliser un tableau de boolean : pour cela, il y a classe BitArray qui ne consommera qu’un bit par booléen.
Mitsu
Encore merci Mitsu pour toutes ces explications.