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 :

typedef union en C++


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    45
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 45
    Points : 34
    Points
    34
    Par défaut typedef union en C++
    j'ai déclaré une structure; un registre 48 bit accessible chaque 6 bits comme suit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    typedef struct reg48 {
    	unsigned int   b7:6 ,b6:6 ,b5:6 ,b4:6, b3:6, b2:6, b1:6, b0:6;
    } reg48;
     
    typedef union reg48_6 {
    	unsigned __int64 reg;// pour un accés à toute la valeurs
        reg48 b;
    }reg48_6;
    Et quand j'utilise ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    s.reg=0x5bdadd934fad;
    	cout<<hex<<s.reg<<"\n";
        cout<<hex<<s.b.b0<<" "<<s.b.b1<<" "<<s.b.b2<<" "<<s.b.b3<<" "<<s.b.b4<<" "<<s.b.b5;
        cout<<" "<<hex<<s.b.b6<<" "<<s.b.b7<<"\n";
     
    s.reg=0x5bda5d934fad;
    	cout<<hex<<s.reg<<"\n";
        cout<<hex<<s.b.b0<<" "<<s.b.b1<<" "<<s.b.b2<<" "<<s.b.b3<<" "<<s.b.b4<<" "<<s.b.b5;
        cout<<" "<<hex<<s.b.b6<<" "<<s.b.b7<<"\n";
    ça me donne la même décomposition pour les 2 DIFFERENTS NOMBRES!!, et elle est fausse!!!.
    j'ai affiché en binaire, j'ai remarqué que 2 bits aux milieu sont ignoré!!!

    je ne comprend pas pourquoi?
    COMMENT FAIRE UNE DéCOMPOSITION DE MON REGISTRE?

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    ça ressemble à une "erreur" de padding.
    Le padding par défaut est sûrement 8.

    #pragma pack(1) devrait corriger ça.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Nouveau membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    45
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 45
    Points : 34
    Points
    34
    Par défaut
    ça veut dire quoi ça?
    Ou est ce que je le rajoute ce "#pragma pack(1)"?

    en plus,ma décomposition marche pour quelques nombres et non pour d'autres!!!!!!!

  4. #4
    Membre éclairé
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Points : 719
    Points
    719
    Par défaut
    Comme dit Bousk ça peut être un problème de padding.
    Et effectivement, en ce cas, rajouter #pragma pack(1) avant la definition de ta structure devrait aider.

    Sinon une autre solution pour décomposer ton registre consiste à faire des décalages (avec l'opérateur >>) et des masks (avec l'opérateur &).

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    D'après la norme, si tu écris dans un champ d'une union, tu n'as pas le droit de lire un autre champ.

    Après, certains compilateurs permettent de faire ce que tu fais (je crois que visual C++ le permet spécifiquement, du moins dans certaines situations), mais c'est du comportement non standard (qui plus est, ça risque de marcher pendant longtemps, puis de planter juste à la veille d'une livraison, parce qu'une autre modif ailleurs change la manière dont le compilateur optimise son code).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Citation Envoyé par Bousk Voir le message
    #pragma pack(1) devrait corriger ça.
    Non. #pragma pack(1) ne concerne pas les champs de bits.

    Citation Envoyé par dida_plt Voir le message
    je ne comprend pas pourquoi?
    COMMENT FAIRE UNE DéCOMPOSITION DE MON REGISTRE?
    Avec Visual C++2010, tu dois déclarer ton champ de bits avec un type suffisant pour contenir l'ensemble de tes bits :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    typedef struct reg48 {
    	unsigned __int64   b7:6 ,b6:6 ,b5:6 ,b4:6, b3:6, b2:6, b1:6, b0:6;
    } reg48;
     
    typedef union reg48_6 {
       unsigned __int64 reg;// pour un accés à toute la valeurs
        reg48 b;
    }reg48_6;
    Mais cela ne fait que retarder d'éventuels problèmes...


    Outre la remarque de Loïc sur l'union, j'en rajoute une sur les champs de bits pour décourager leur utilisation.

    Lorsque tu as plusieurs champs de bits consécutifs (comme les tiens), l'organisation mémoire est la suivante :
    => L'ordre de rangement des bits (gauche à droite ou droite à gauche) est dépendant de l'implémentation
    => Le premier champ de bits est rangé dans un espace du type sous-jacent.
    => Ensuite le champ de bits suivant est ajouté à la suite s'il rentre en entier.
    => Lorsque le champ de bits suivant chevauche plusieurs espace du type sous-jacent, le comportement est dépendant du compilateur/plateforme cible qui peut soit mettre le nouveau champ entièrement dans un nouvel espace du type sous-jacent, soit répartir le champ de bit entre les deux éléments.
    Exemple en supposant un type sur un 1 octet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct expl
    {
       unsigned char champ1 : 6;
       unsigned char champ2 : 6;
    };
    On peut avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    champ1 :                                           X   X   X  X   X  X
    champ2 :                  X   X  X  X   X   X
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    champ1 :                                           X   X   X  X   X  X
    champ2 :          X  X  X   X  X   X
    Ce qui constitue un problème évident à la portabilité de ton code...

    Je te conseille donc d'utiliser plutôt des fonctions de manipulation de bits pour les lire et les écrire à partir d'un type sous-jacent que tu maîtrise.
    Si tu peux mapper l'intégralité des bits dans un seul objet du type sous-jacent alors fais le (ce qui est le cas ici en changeant unsigned int en unsigned __int64 (*)). Sinon, préfères la manipulation de bits (décalages et masques).

    (*) ceci dit cela n'enlève rien au problème souligné par Loïc.

  7. #7
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Pour reprendre sur les champs de bits (ces remarques me font peur). Est-il quand même "légal" d'utiliser des champs de bits, lorsqu'on ne se préoccupe pas de leur place mémoire (donc pas d'union avec un type pour tous les accéder), et qu'on laisse le compilateur optimiser comme il veut du moment que le comportement indépendant des champs de bits reste le bon ?

    C'est-à-dire, est-ce que la structure reg48 citée plus haut, si l'on omet l'union reg48_6, est "légale" en soi ?

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Pour reprendre sur les champs de bits (ces remarques me font peur). Est-il quand même "légal" d'utiliser des champs de bits, lorsqu'on ne se préoccupe pas de leur place mémoire (donc pas d'union avec un type pour tous les accéder), et qu'on laisse le compilateur optimiser comme il veut du moment que le comportement indépendant des champs de bits reste le bon ?

    C'est-à-dire, est-ce que la structure reg48 citée plus haut, si l'on omet l'union reg48_6, est "légale" en soi ?
    Oui, tant que l'on ait en réalité besoin de huit fois six bits distincts, et si l'on est conscient du fait qu'il peut très facilement y avoir certains endroits où (minimum) deux bits surnuméraires seront ajoutés de manière à assurer un alignement correct

    La meilleure solution consiste donc sans doute à créer une "moulinette" qui s'occupera d'évacuer les bits inutiles à coup de décalage de bits, ou mieux, de recourir à boost::bitset qui, pour autant que je le sache, permet de gérer correctement les problèmes de padding
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Citation Envoyé par koala01 Voir le message
    Oui, tant que l'on ait en réalité besoin de huit fois six bits distincts, et si l'on est conscient du fait qu'il peut très facilement y avoir certains endroits où (minimum) deux bits surnuméraires seront ajoutés de manière à assurer un alignement correct
    Le soucis c'est qu'on ne sais pas si le compilateur insère des bits de padding ou s'il fait se chevaucher le champs de bits dans les deux objets sous-jascent. Si ce n'était que du padding, on le saurait et on ferait avec...

    Citation Envoyé par Ekleog Voir le message
    Pour reprendre sur les champs de bits (ces remarques me font peur). Est-il quand même "légal" d'utiliser des champs de bits, lorsqu'on ne se préoccupe pas de leur place mémoire (donc pas d'union avec un type pour tous les accéder), et qu'on laisse le compilateur optimiser comme il veut du moment que le comportement indépendant des champs de bits reste le bon ?

    C'est-à-dire, est-ce que la structure reg48 citée plus haut, si l'on omet l'union reg48_6, est "légale" en soi ?
    Bon, les champs de bits, j'en ai vu deux usages (principalement en C) :
    1/ Le premier, c'est dans l'embarqué dans la chasse à l'octet. On cherche vraiment à avoir des structures à la taille minimum car tout octet est bon à prendre. Là, cela suppose aussi qu'on a qu'une cible et qu'on connait très très bien son compilateur et ses options.

    2/ Le second, cela semble être le cas ici, concerne le mapping mémoire de registres. Chaque bit ou groupe de bits correspond à une commande, à un paramétrage ou une information bien particulière. Il est intéressant de pouvoir travailler avec des champs de bits car non seulement cela est cohérent avec la description du registre mais certains micros ont des instructions pour travailler au niveau du bit et le registre l'accepte.

    Faut-il utiliser des champs de bit ? Il est clair que le cas 1/ ne s'applique absolument pas à un PC (ou un mobile ou tout système qui tourne avec un OS 'riche' type windows CE/XP/7 ou linux). Sur de l'embarqué dur avec quelques kilo de ram et à peine plus de flash, c'est parfois indispensable.
    Le second cas est intéressant mais il faut alors faire coïncider la taille du type sous-jacent à la taille du registre adressé. L'idée est de ne pas avoir de chevauchement entre deux objets sous-jacent. Et même de n'avoir carrément qu'un seul objet du type sous-jacent correspondant à la taille du registre adressé.

    L'utilisation des champs de bits dans ces deux cas est directement liée à une connaissance et une volonté d'agir en cohérence avec une certaine organisation de la mémoire. Par conséquent, pour répondre strictement à ta question
    Est-il quand même "légal" d'utiliser des champs de bits, lorsqu'on ne se préoccupe pas de leur place mémoire
    Ben dans ce cas, n'utilises pas de champs de bits. Utilise, par exemple comme le propose Koala, Boost.Dynamic BitsetMon message précédent est effectivement mal conclu. Utiliser des champ de bit restera une solution maîtrisée si tous les bits tiennent dans un seul objet du type sous-jacent. Ce qu'il faut éviter, c'est d'avoir nécessité de plusieurs objets et surtout du chevauchement.

  10. #10
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    (Je le dis tout de suite, n'hésitez pas à corrger ma syntaxe des bitsets, je n'en ai encore jamais vraiment utilisé.)

    En pensant à un champ de bits, je pense instinctivement à un ensemble de bool.
    C'est-à-dire, pourquoi utiliser ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct config { // Totalement imaginaire, ne me hurlez pas dessus !
      bool isAvailable, isValid; // Pas d'autres idées
    };
    Quand un code comme le suivant gagne 7 octets gratuitement sans contre-partie ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct config {
      bool isAvailable:1, isValid:1;
    };
    Un autre exemple est lorsqu'on a besoin d'un wrapping à N bits. (Si c'est bien le cas, j'ai peu l'usage des champs de bits)
    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct normal { unsigned int val; };
    struct bitset { unsigned int val:4; };
     
    // ...
    normal n;
    bitset b;
     
    n.val  = 5; b.val  = 5;
    n.val *= 5; b.val *= 5;
     
    assert(n.val == 25 && b.val == 9); // ici
    Pour, finalement, boost.dynamic_bitset, il me semble qu'il ne dispose pas d'une syntaxe aussi simple que "bs.isAvailable" ... Si ?

  11. #11
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Le gain n'est pas sans contrepartie. On gagne en mémoire (1 octet, dans ton exemple, selon toute probabilité, et non pas 7), éventuellement, mais on perd en coût de manipulation.

    Sinon, sans même parler de boost::dynamic_bitset, il y a déjà std::bitset qui gère les choses très simplement. Genre if(b.test(3)) ou if(b[3]).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  12. #12
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Le gain n'est pas sans contrepartie. On gagne en mémoire (1 octet, dans ton exemple, selon toute probabilité, et non pas 7), éventuellement, mais on perd en coût de manipulation.
    Il me semble qu'un bool est codé sur la taille d'un int en général, non ? (D'ailleurs, ça m'avait étonné quand j'avais appris ça.)
    Mais quel est ce coût en manipulation dont on parle ? L'utilisation finale est exactement la même pour les deux structures, excepté le ":1", non ?

    Citation Envoyé par JolyLoic Voir le message
    Sinon, sans même parler de boost::dynamic_bitset, il y a déjà std::bitset qui gère les choses très simplement. Genre if(b.test(3)) ou if(b[3]).
    Le souci est alors le manque de logique sémantique à b[3]. Parce que, que signifie ce "3" ?
    Et ajouter soit une classe pour enrober bitset, soit une constante, ne fait que complexifier le code pour l'utilisateur.

  13. #13
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Il me semble qu'un bool est codé sur la taille d'un int en général, non ? (D'ailleurs, ça m'avait étonné quand j'avais appris ça.)
    Ce n'est plus le cas depuis des lustres sous visual C++ (c'est passé de 4 bytes à 1 dans la version 5). Pour gcc, je crois que c'est aussi 1 (mais je n'ai pas vérifié). Mais c'est vrai que le compilateur a le choix.
    Citation Envoyé par Ekleog Voir le message
    Mais quel est ce coût en manipulation dont on parle ? L'utilisation finale est exactement la même pour les deux structures, excepté le ":1", non ?
    Les opérations pour accéder à un bit sont plus couteuses au niveau processeur que pour accéder à un byte dans sa globalité.
    Citation Envoyé par Ekleog Voir le message
    Le souci est alors le manque de logique sémantique à b[3]. Parce que, que signifie ce "3" ?
    Et ajouter soit une classe pour enrober bitset, soit une constante, ne fait que complexifier le code pour l'utilisateur.
    En effet, j'utiliserais plutôt b[READ_ONLY] = true; que b[3] = true;
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  14. #14
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Ce n'est plus le cas depuis des lustres sous visual C++ (c'est passé de 4 bytes à 1 dans la version 5). Pour gcc, je crois que c'est aussi 1 (mais je n'ai pas vérifié). Mais c'est vrai que le compilateur a le choix.
    En effet, sous g++ 4.6.2 sizeof(bool) = 1. Donc mea culpa !

    Les opérations pour accéder à un bit sont plus couteuses au niveau processeur que pour accéder à un byte dans sa globalité.
    Sauf qu'utiliser un champ de bits ou un bitset, le champ de bits reste probablement plus rapide ou égal : bitset ne peut probablement être plus optimisé qu'une construction présente depuis des lustres et moins générique !

    En effet, j'utiliserais plutôt b[READ_ONLY] = true; que b[3] = true;
    Imaginons ça dans le cas d'une configuration de boost.filesystem ...
    Oups, le namespace fait peut !

    Et j'ai (personnellement) l'impression que c'est plus user-friendly d'utiliser b.read_only = true que b[read_only] = true - la première étant totalement sans ambiguïté.

  15. #15
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Citation Envoyé par Ekleog Voir le message
    Sauf qu'utiliser un champ de bits ou un bitset, le champ de bits reste probablement plus rapide ou égal : bitset ne peut probablement être plus optimisé qu'une construction présente depuis des lustres et moins générique !
    Non. Si le µ n'a pas d'instruction pour manipuler au niveau du bit, alors le code généré est plus grand en taille et en temps tout simplement car il doit utiliser un masque et/ou un décalage de bit. En optimisation, le coût d'un std::bitset peut être nul car les fonctions sont inlinées.
    Exemple avec visual en compilant en release (/Ox /Ot):
    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
    30
    31
    32
    33
    34
    35
    #include <bitset>
    #include <iostream>
     
    struct dummy
    {
       unsigned char :4;
       unsigned char bitf : 1;
    };
    int main()
    {
       int i=1;
       dummy d;
       d.bitf = 1;
       std::bitset<1> bs;
       bs[0] = 1;
     
       char c;
       std::cin>>c;
       int j;
     
       switch(c)
       {
       case 'i':
          j = i;
          break;
       case 'd':
          j = d.bitf;
          break;
       case 'b':
          j = bs[0];
          break;
       }
       std::cout<<j<<"\n";
       return 0;
    }
    donne :
    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
       {
       case 'i':
          j = i;
    00AE1039  mov         eax,1  
          break;
    00AE103E  jmp         main+4Fh (0AE104Fh)  
       case 'd':
          j = d.bitf;
    00AE1040  movzx       eax,bl  
    00AE1043  shr         eax,4  
    00AE1046  and         eax,1  
          break;
    00AE1049  jmp         main+4Fh (0AE104Fh)  
       case 'b':
          j = bs[0];
    00AE104B  mov         eax,dword ptr [esp+0Ch]  
          break;
       }
    Clairement, le bitset n'a plus qu'une instruction là où le champ de bit en a 3

  16. #16
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Et j'ai (personnellement) l'impression que c'est plus user-friendly d'utiliser b.read_only = true que b[read_only] = true - la première étant totalement sans ambiguïté.
    b.IsReadonly() ne présuppose rien sur l'implémentation derrière

  17. #17
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Salut,

    Non. Si le µ n'a pas d'instruction pour manipuler au niveau du bit, alors le code généré est plus grand en taille et en temps tout simplement car il doit utiliser un masque et/ou un décalage de bit. En optimisation, le coût d'un std::bitset peut être nul car les fonctions sont inlinées.
    Le µ ?
    Sinon, le bitset nécessite également un masque et/ou un décalage de bit, non ?
    Exemple avec visual en compilant en release (/Ox /Ot):
    [...]
    Clairement, le bitset n'a plus qu'une instruction là où le champ de bit en a 3
    (Comment as-tu un aussi joli output ?)
    Sinon, le test est biaisé : tu as ajouté unsigned char :4 pour décaler bitf, ce qui n'est pas le cas pour bitset !
    Tricheur !

    Edit : Pour "b.IsReadonly", on est d'accord, c'est une meilleur idée en général - mais je pense pour le moment surtout à la compréhension du code caché dans cette fonction. Par ailleurs, on parle surtout de configuration pour le moment, et "b.SetReadonly(true)" me semble (toujours personnellement, mais là c'est moins marqué) moins syntaxiquement correct que "b.readonly = true".

  18. #18
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Le µ ?
    micro (on n'étudie plus le grec au lycée )

    Citation Envoyé par Ekleog Voir le message
    (Comment as-tu un aussi joli output ?)
    fenêtre assembleur de visual + copier/coller.

    Citation Envoyé par Ekleog Voir le message
    Sinon, le test est biaisé : tu as ajouté unsigned char :4 pour décaler bitf, ce qui n'est pas le cas pour bitset !
    Tricheur !
    dsl mais ça ne change pas :
    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
    30
    31
    32
    33
    34
    35
    #include <bitset>
    #include <iostream>
     
    struct dummy
    {
       unsigned char :4;
       unsigned char bitf : 1;
    };
    int main()
    {
       int i=1;
       dummy d;
       d.bitf = 1;
       std::bitset<5> bs;
       bs[4] = 1;
     
       char c;
       std::cin>>c;
       int j;
     
       switch(c)
       {
       case 'i':
          j = i;
          break;
       case 'd':
          j = d.bitf;
          break;
       case 'b':
          j = bs[4];
          break;
       }
       std::cout<<j<<"\n";
       return 0;
    }
    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
       {
       case 'i':
          j = i;
    00861039  mov         eax,1  
          break;
    0086103E  jmp         main+4Fh (86104Fh)  
       case 'd':
          j = d.bitf;
    00861040  movzx       eax,bl  
    00861043  shr         eax,4  
    00861046  and         eax,1  
          break;
    00861049  jmp         main+4Fh (86104Fh)  
       case 'b':
          j = bs[4];
    0086104B  mov         eax,dword ptr [esp+0Ch]  
          break;
       }
    bitset 2 - bitfield 0

    [edit]
    d'ailleurs
    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
    30
    31
    32
    33
    34
    #include <bitset>
    #include <iostream>
     
    struct dummy
    {
       unsigned char bitf : 1;
    };
    int main()
    {
       int i=1;
       dummy d;
       d.bitf = 1;
       std::bitset<5> bs;
       bs[4] = 1;
     
       char c;
       std::cin>>c;
       int j;
     
       switch(c)
       {
       case 'i':
          j = i;
          break;
       case 'd':
          j = d.bitf;
          break;
       case 'b':
          j = bs[4];
          break;
       }
       std::cout<<j<<"\n";
       return 0;
    }
    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
       {
       case 'i':
          j = i;
    010A1039  mov         eax,1  
          break;
    010A103E  jmp         main+4Ch (10A104Ch)  
       case 'd':
          j = d.bitf;
    010A1040  movzx       eax,bl  
    010A1043  and         eax,1  
          break;
    010A1046  jmp         main+4Ch (10A104Ch)  
       case 'b':
          j = bs[4];
    010A1048  mov         eax,dword ptr [esp+0Ch]  
          break;
       }
    reste toujours le masque !

  19. #19
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    micro (on n'étudie plus le grec au lycée )
    Si, et le grec me dit mu.
    Du coup, je n'avais pas pensé à microprocesseur.

    fenêtre assembleur de visual + copier/coller.
    Ah, zut, n'étant pas sous windows je ne pensais pas à VS.

    dsl mais ça ne change pas :
    [...]
    bitset 2 - bitfield 0
    Reste à vérifier si le déréférencement d'un pointeur (donc appel mémoire) n'est pas plus long que l'application d'un masque à un registre.

    [edit]
    d'ailleurs
    [...]
    reste toujours le masque !
    Là, c'est un manque d'optimisation assez flagrant, non ?
    Parce que d ne peut pas être modifié par autre chose que bitf, et donc les autres bits ne peuvent pas être mis à autre chose que 0.

    D'ailleurs, g++ -std=c++0x -O2 -Wall -Wextra -pedantic -c -g -Wa,-ahl=test.s test.cpp me donne comme assembleur (coupé) :
    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
      20:b.cpp         ****    switch(c)
      21:b.cpp         ****    {
      22:b.cpp         ****    case 'i':
      23:b.cpp         ****       j = i;
      24:b.cpp         ****       break;
      25:b.cpp         ****    case 'd':
      26:b.cpp         ****       j = d.bitf;
      27:b.cpp         ****       break;
      28:b.cpp         ****    case 'b':
      29:b.cpp         ****       j = bs[4];
    GAS LISTING /tmp/ccJB241V.s 			page 2
     
     
      30:b.cpp         ****       break;
      31:b.cpp         ****    }
      32:b.cpp         ****    std::cout<<j<<"\n";
      26              		.loc 1 32 0
      27 0013 BE010000 		movl	$1, %esi ###############################################
      27      00
      28 0018 BF000000 		movl	$_ZSt4cout, %edi
      28      00
      29 001d E8000000 		call	_ZNSolsEi
      29      00
      30 0022 BE000000 		movl	$.LC0, %esi
      30      00
      31 0027 4889C7   		movq	%rax, %rdi
      32 002a E8000000 		call	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    Donc il optimise complètement les deux appels.

    Par ailleurs, -O1 fait pareil, et -O0 pollue le code avec toutes les fonctions de bitset ; et je dois admettre n'être pas capable de comprendre l'assembleur (mais je coupe le bout intéressant) :
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
      20:b.cpp         ****    switch(c)
     150              		.loc 2 20 0
     151 0067 0FB645BF 		movzbl	-65(%rbp), %eax
     152 006b 0FBEC0   		movsbl	%al, %eax
     153 006e 83F864   		cmpl	$100, %eax
     154 0071 7414     		je	.L7
     155 0073 83F869   		cmpl	$105, %eax
     156 0076 7407     		je	.L8
     157 0078 83F862   		cmpl	$98, %eax
     158 007b 7419     		je	.L6
     159 007d EB4E     		jmp	.L5
     160              	.L8:
      21:b.cpp         ****    {
      22:b.cpp         ****    case 'i':
      23:b.cpp         ****       j = i;
     161              		.loc 2 23 0
     162 007f 8B45F8   		movl	-8(%rbp), %eax
     163 0082 8945FC   		movl	%eax, -4(%rbp)
      24:b.cpp         ****       break;
     164              		.loc 2 24 0
     165 0085 EB46     		jmp	.L5
     166              	.L7:
      25:b.cpp         ****    case 'd':
      26:b.cpp         ****       j = d.bitf;
     167              		.loc 2 26 0
     168 0087 0FB645CF 		movzbl	-49(%rbp), %eax
     169 008b 83E001   		andl	$1, %eax
     170 008e 0FB6C0   		movzbl	%al, %eax
     171 0091 8945FC   		movl	%eax, -4(%rbp)
      27:b.cpp         ****       break;
     172              		.loc 2 27 0
     173 0094 EB37     		jmp	.L5
     174              	.L6:
      28:b.cpp         ****    case 'b':
      29:b.cpp         ****       j = bs[4];
     175              		.loc 2 29 0
     176 0096 488D45E0 		leaq	-32(%rbp), %rax
     177 009a 488D4DC0 		leaq	-64(%rbp), %rcx
     178 009e BA040000 		movl	$4, %edx
     178      00
     179 00a3 4889CE   		movq	%rcx, %rsi
     180 00a6 4889C7   		movq	%rax, %rdi
     181 00a9 E8000000 		call	_ZNSt6bitsetILm5EEixEm
    GAS LISTING /tmp/ccqQKvzV.s 			page 12
     
     
     181      00
     182 00ae 488D45E0 		leaq	-32(%rbp), %rax
     183 00b2 4889C7   		movq	%rax, %rdi
     184 00b5 E8000000 		call	_ZNKSt6bitsetILm5EE9referencecvbEv
     184      00
     185 00ba 0FB6C0   		movzbl	%al, %eax
     186 00bd 8945FC   		movl	%eax, -4(%rbp)
     187 00c0 488D45E0 		leaq	-32(%rbp), %rax
     188 00c4 4889C7   		movq	%rax, %rdi
     189 00c7 E8000000 		call	_ZNSt6bitsetILm5EE9referenceD1Ev
     189      00
      30:b.cpp         ****       break;
     190              		.loc 2 30 0
     191 00cc 90       		nop
     192              	.L5:
      31:b.cpp         ****    }
    Il me semble qu'en moyenne la solution non-bitset gagne : 1-1 (et g++ gagne le concours d'optimisation !)

    Par ailleurs, en reprenant le bit 5 (unsigned char :4;), j'arrive à nouveau avec -O2 et -O1 à une optimisation totale (et -O0 gagne à nouveau de peu).

    Enfin, en prenant un programme plus complexe :
    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
    30
    31
    32
    #include <bitset>
    #include <iostream>
     
    struct dummy
    {
       unsigned char :4;
       unsigned char bitf : 1;
    };
    int main()
    {
       int i;
       dummy d;
       std::bitset<5> bs;
     
       char c; bool b;
       std::cin>>c>>b;
     
       switch(c)
       {
       case 'i':
          i = b;
          break;
       case 'd':
          d.bitf = b;
          break;
       case 'b':
          bs[4] = b;
          break;
       }
       std::cout<<i<<d.bitf<<b<<"\n";
       return 0;
    }
    Désassemblé avec gdb (test.s avait un résultat assez incompréhensible), je n'ai pas quelque chose de plus compréhensible : il semblerait que g++ ait décidé de brouiller les pistes en réordonnnt les blocs et en rendant main() imbuvable :
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    Dump of assembler code for function main:
       0x0000000000400880 <+0>:     push   rbp
       0x0000000000400881 <+1>:     mov    edi,0x6012a0
       0x0000000000400886 <+6>:     push   rbx
       0x0000000000400887 <+7>:     sub    rsp,0x18
       0x000000000040088b <+11>:    lea    rsi,[rsp+0xe]
       0x0000000000400890 <+16>:    call   0x400870 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RS3_@plt>
       0x0000000000400895 <+21>:    lea    rsi,[rsp+0xf]
       0x000000000040089a <+26>:    mov    rdi,rax
       0x000000000040089d <+29>:    call   0x400850 <_ZNSi10_M_extractIbEERSiRT_@plt>
       0x00000000004008a2 <+34>:    movzx  eax,BYTE PTR [rsp+0xe]
       0x00000000004008a7 <+39>:    cmp    al,0x64
       0x00000000004008a9 <+41>:    je     0x4008ed <main+109>
       0x00000000004008ab <+43>:    xor    ebx,ebx
       0x00000000004008ad <+45>:    xor    esi,esi
       0x00000000004008af <+47>:    cmp    al,0x69
       0x00000000004008b1 <+49>:    je     0x4008f9 <main+121>
       0x00000000004008b3 <+51>:    movzx  ebp,BYTE PTR [rsp+0xf]
       0x00000000004008b8 <+56>:    mov    edi,0x6013c0
       0x00000000004008bd <+61>:    call   0x4007e0 <_ZNSolsEi@plt>
       0x00000000004008c2 <+66>:    movsx  esi,bl
       0x00000000004008c5 <+69>:    mov    rdi,rax
       0x00000000004008c8 <+72>:    call   0x400820 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>
       0x00000000004008cd <+77>:    mov    esi,ebp
       0x00000000004008cf <+79>:    mov    rdi,rax
       0x00000000004008d2 <+82>:    call   0x400860 <_ZNSo9_M_insertIbEERSoT_@plt>
       0x00000000004008d7 <+87>:    mov    esi,0x400b0c
       0x00000000004008dc <+92>:    mov    rdi,rax
       0x00000000004008df <+95>:    call   0x400840 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
       0x00000000004008e4 <+100>:   add    rsp,0x18
       0x00000000004008e8 <+104>:   xor    eax,eax
       0x00000000004008ea <+106>:   pop    rbx
       0x00000000004008eb <+107>:   pop    rbp
       0x00000000004008ec <+108>:   ret    
       0x00000000004008ed <+109>:   movzx  ebx,BYTE PTR [rsp+0xf]
       0x00000000004008f2 <+114>:   xor    esi,esi
       0x00000000004008f4 <+116>:   and    ebx,0x1
       0x00000000004008f7 <+119>:   jmp    0x4008b3 <main+51>
       0x00000000004008f9 <+121>:   movzx  esi,BYTE PTR [rsp+0xf]
       0x00000000004008fe <+126>:   xor    ebx,ebx
       0x0000000000400900 <+128>:   jmp    0x4008b3 <main+51>
    Je dois admettre y comprendre peu, mais il me semble que bitset y gagne un and.

    Bref, en gros c'est égalité entre les deux. De là, pourquoi ne pas choisir celui dont la syntaxe nous semble la plus correcte ?

  20. #20
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    De là, pourquoi ne pas choisir celui dont la syntaxe nous semble la plus correcte ?
    parce que les champs de bit sur PC n'ont que peu d'intérêt, sont sources d'erreur et souvent mal maîtrisés ?

Discussions similaires

  1. comment est la memoire avec typedef union
    Par gronaze dans le forum C
    Réponses: 4
    Dernier message: 27/02/2007, 14h22
  2. UNION qui ne fonctionne pas
    Par r-zo dans le forum Langage SQL
    Réponses: 7
    Dernier message: 21/07/2003, 10h04
  3. Réponses: 6
    Dernier message: 26/01/2003, 13h45
  4. Créer une vue pour trier une requete UNION ?
    Par Etienne Bar dans le forum SQL
    Réponses: 3
    Dernier message: 03/01/2003, 20h22
  5. Sortir un typedef d'une classe
    Par Theophil dans le forum C++Builder
    Réponses: 13
    Dernier message: 03/07/2002, 17h21

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