IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Taille d'un bool en mémoire = 4 octets ? Mot mémoire X86


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    1 002
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 002
    Par défaut Taille d'un bool en mémoire = 4 octets ? Mot mémoire X86
    Bonjour,

    J'ai une question de culture geek:

    Il y a quelques temps j'avais lu un thread parlant de l'optimisation et de l'alignement mémoire.
    Et un type disait que sur X86 la plus petite unité qu'on pouvait allouer physiquement c’était 4 octets
    Ce qui fait que physiquement en RAM un int ou un bool occupent la même place sur une archi X86. Même si leur représentation est différente.

    Est ce vrai en C et en Csharp ? Je n'arrive pas à trouver les sources qui confirment ca. C'est plus flou en .Net / qui passe par une VM.

    source du thread avec un type sérieux Mac LAK:
    http://www.developpez.net/forums/d75...nce-decevante/
    Commence déjà par analyser la dispersion en mémoire de tes éléments, ainsi que leur alignement. N'oublie pas : pour lire un octet en RAM, tu en lis TOUJOURS au minimum quatre, le mot-mémoire complet... Mais tu n'en utilises qu'un seul, ce qui est une perte de temps lourde si jamais tu as des invalidations de cache qui se goupillent mal.
    Merci pour vos lumières

  2. #2
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    la plus petite unité qu'on pouvait allouer physiquement c’était 4 octets
    C'est vrai pour des variable bool qu'on allouerait indépendament les unes des autres, du fait que les allocations mémoires utilisent un alignement sur les mots machines (4 ou 8 octets).

    Toutefois, si on en définit plusieurs dans une même classe ou une même structure, comme l'allocation mémoire se fait de façon globale, il ne prennent plus qu'un octet chacun.

  3. #3
    Membre extrêmement actif
    Inscrit en
    Avril 2008
    Messages
    2 573
    Détails du profil
    Informations personnelles :
    Âge : 65

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 573
    Par défaut
    bonjour

    Peut etre le "type" en question s'est mal exprime...
    Il voulait dire en fait la plus petite unite adressable(et non allouable) est 4 octets en x86
    Car le registre d'adresse est de 4 octets(c'est un ptr sur 4 octets)...

    Se conferer à IntPtr (adresse des objets unmanaged) en c#...
    Ensuite ayant obtenu l'adresse(IntPtr) d'un Object, pour parcourir ses octets il faut utiliser un Offset par rapport au IntPtr....
    L'unite d'Offset est le byte....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    // talle de l'objet 
      int size = Marshal.SizeOf(managedArray[0]) * managedArray.Length;
     
      // on s'alloue une zone memoire unmanaged et un ptr dessus
     
      IntPtr pnt = Marshal.AllocHGlobal(size);
     
      //on  accede à chaque octet(offset =1)
      for (int i = 0; i < managedArray.Length ; i++)
      {
           byte   b = Marshal.ReadByte(pnt);
            //tache   a faire       
            .....................
           // progresse d'un octet dans l'exemple        
           pnt =  IntPtr.Add(pnt,1);
     
      }
     
      //on  accede  tous les 2 octets(offset=2) 
      for (int i = 0; i < managedArray.Length ; i++)
      {
           Int16   k = Marshal.ReadInt16(pnt);
            //tache   a faire       
            .....................
           //on progresse de 2 octets        
           pnt =  IntPtr.Add(pnt,2);
     
      }
    Ceci justifie le fait en c# sharp que un IntPtr est sur 4 octets...


    En code unsafe le pointeur est toujours sur 4 octets
    Mais il n'y a pas d'offset ce qui oblige à caster l'Objet sur un pointer de type byte si on veut avancer par byte...sur un Int16 ou UInt16 si on veut des sauts de 2 bytes etc...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
     
          int number = 1024;
     
            unsafe 
            {
                // Convert to byte:
                byte* p = (byte*)&number;
     
                System.Console.Write("The 4 bytes of the integer:");
     
                // Display the 4 bytes of the int variable:
                for (int i = 0 ; i < sizeof(int) ; ++i)
                {
                    System.Console.Write(" {0:X2}", *p);
                    // Increment the pointer:
                    p++;
                }
                System.Console.WriteLine();
                System.Console.WriteLine("The value of the integer: {0}", number);
            }


    bon code....

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    1 002
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 002
    Par défaut
    Merci les mecs pour vos réponses !
    Je ne suis pas certain que celles ci apportent plus de solution que de nouvelles interrogations pour a part haha

    Bon faut que je fasse une list massive de bool et d'int sur un programme simple, calculer la différence que prennent les 2 programmes pour en déduire les mecanismes...en x86 et x64

  5. #5
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    1 002
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 002
    Par défaut
    Bon voila quelques résultat en release 4.5 .Net (après faut les interpréter)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        class Program
        {
            static void Main(string[] args)
            {
                List<bool> l = new List<bool>();
                for (int i = 0; i < 100000000; ++i)
                {
                    l.Add(true);
                }
                Console.WriteLine("finish");
                Console.ReadLine(); // je relève au niveau du process explorer les Mo
                l.Add(true);
            }
        }
    x86
    bool 161,4 Mo
    short 321,3 Mo
    int 641,1 Mo
    decimal c 'est bien 4 fois plus grand en memoire

    x64
    bool 225,4 Mo
    short 321,7 Mo
    int 641,4 Mo
    decimal c 'est bien 4 fois plus grand en memoire


    Graffito ce test semble confirmer ton explication ! Pourquoi 1 octet ? c est le mot memoire de la machine virtuelle .net ? Ou bien tout simplement le mot mémoire tout court ?

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    1 002
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 002
    Par défaut
    Et en passant par une classe intermediaire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        class Program
        {
            static void Main(string[] args)
            {
                List<ClassTest> l = new List<ClassTest>();
                for (int i = 0; i < 10000000; ++i)
                {
                    l.Add(new ClassTest(true));
                }
                Console.WriteLine("finish");
                Console.ReadLine();
                l.Add(new ClassTest(true));
            }
        }

    Classe:

    x86
    bool 187,3 Mo
    short 187,3
    int 187,3
    decimal 304,9 (Bon ba là je comprend pu rien)

    x64
    bool 362,7 Mo
    short 362,6
    int 362,7
    decimal 439,9 (Bon ba là je comprend pu rien)


    Et pour une structure (résultat similaire au type valeur sans surprise):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
        public struct StrucTest
        {
            public bool v;
        }
     
        class Program
        {
            static void Main(string[] args)
            {
                List<StrucTest> l = new List<StrucTest>();
                StrucTest s;
                for (int i = 0; i < 100000000; ++i)
                {
                    s = new StrucTest();
                    s.v = true;
                    l.Add(s);
                }
                Console.WriteLine("finish");
                Console.ReadLine();
                s = new StrucTest();
                s.v = true;
                l.Add(s);
            }
        }
    x86
    bool 161,4 Mo
    short 321,3
    int 641

    x64
    bool 225,5 Mo
    short 362,7
    int 641,5

  7. #7
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Bonjour.


    a) Pour minimiser le nombre de circuits les processeurs réalisent toujours leurs opérations sur des nombres de bits bien spécifiques : nos entiers font 1, 2, 4 ou 8 octets, ceux en virgule flottante 4, 8, ou 10 octets, etc. Il n'y a pas d'instructions pour des entiers de 3 octets par exemple.

    Et bien logiquement il en va de même pour les opérations de chargement dans les registres : la plupart des processeurs peuvent charger un seul octet à la fois. Les processeurs RISC peuvent en plus charger 4 ou 8 octets à la fois, selon qu'ils sont 32 ou 64 bits. Les processeurs x86 permettent quant à eux de charger 1, 2 ou 4 octets à la fois (et 1, 2, 4 ou 8 sur x64).

    Et si l'on veut lire un entier 16-bits sur RISC 32-bits ? Il faut lire 4 octets et en tronquer deux !



    b) Par ailleurs les processeurs exigent souvent que les adresses soient alignées, typiquement sur 4 ou 8 octets selon que le processeur est 32 ou 64 bits. Il y a plusieurs raisons à cela, à commencer par le fait que sans alignement imposé lire certaines valeurs nécessiterait deux lectures en mémoire. Par exemple si le processeur lit la mémoire par groupe de 16 octets (taille du bus) et que les données ne sont pas alignées, alors un groupe de 4 octets peut être à cheval sur deux blocs de 16 octets ! La gestion de ce cas compliquerait singulièrement le CPU (ou le code si on gérait ça au niveau logiciel).

    Le x86 autorise en général des adresses non-alignées mais en souffre en termes de performances. Les processeurs RISC les refusent le plus souvent.



    c) Concrètement le compilateur doit donc générer un programme correct et rechercher un équilibre entre le nombre d'instructions et la mémoire (occupation mais aussi bande passante, ce qui influe beaucoup sur les perfs). Et par ailleurs il est limité dans ses capacités d'analyse : parfois le compilateur ne peut déterminer à l'avance par qui telle variable a été déclarée et si elle sera bien alignée, à moins de toujours contraindre les données à être alignées.

    En pratique les objets sont toujours alignés : une classe mesure toujours un multiple de 4 ou 8 octets (32/64 bits). Si une classe ne contient qu'un booléen sur x86-32, alors 3 octets seront laissés vide ! En revanche 2 booléens consécutifs dans une classe ne laisseront bien souvent que 2 octets vides : le compilateur choisira de rajouter des instructions plutôt que de gaspiller la mémoire. De même un tableau de booléens n'occupera qu'un seul octet par booléen, et les booléens d'une fonction seront aussi souvent compactés, d'où tes résultats.



    d) En mode 64-bits les adresses font 8 octets au lieu de 4 pour permettre d'utiliser plus de 4 Go de mémoire (2^32). Note que concernant dotnet, sauf mention explicite, les applis sont 32-bits même sur OS 64 bits : Windows fait la traduction à la volée comme avec tous les programmes 32-bits.



    e) L'opérateur sizeof (en mode unsafe) est un moyen bien plus direct pour mesurer la taille d'un type valeur.



    PS : ta question n'est en rien une pollution, ne t'excuse surtout pas et n'hésite pas à demander.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mémoire vive et adresses mémoire
    Par OKTAH dans le forum Assembleur
    Réponses: 2
    Dernier message: 20/01/2011, 14h59
  2. Fuite mémoire ou pas fuite mémoire?
    Par barbsbou dans le forum Débuter
    Réponses: 8
    Dernier message: 10/03/2008, 14h15
  3. [Mémoire] Acheter une barette mémoire: quel référence ?
    Par condor_01 dans le forum Composants
    Réponses: 7
    Dernier message: 24/03/2007, 00h30
  4. Réponses: 3
    Dernier message: 12/12/2006, 14h40
  5. [JVM] Connaitre la taille mémoire utilisé par les dif classe
    Par sur_uix dans le forum Général Java
    Réponses: 4
    Dernier message: 18/09/2003, 09h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo