11
votes

Est un champ de bits plus efficace (calculé) que des bits de masquage et extraire les données à la main?

J'ai de nombreux petits morceaux de données que je veux pouvoir pousser dans un type de données plus grand. Disons que, hypothétiquement, c'est une date et une heure. La méthode évidente consiste via un champ de bit comme celui-ci.

unsigned hours = stamp.hours;
//versus
unsigned hours = ((dateTime & 0x07C00000)>>21;


2 commentaires

Je mettrais les années dans le mois de forum High Bits Next, etc. Ensuite, lorsque vous souhaitez comparer les objets que vous n'avez pas besoin d'extraire l'année pour les obtenir afin que l'opérateur normal


C'est une astuce assez intelligente, je vais devoir me rappeler dans le futre. Malheureusement, je ne stocke pas les dates et les heures dans le programme que j'écris. Dans mon code, il est utilisé pour stocker l'énergie, le nombre de détecteurs de gamma et une valeur de correction de l'efficacité du détecteur (il ne doit donc pas nécessairement être recalculé d'un petit bâtard complexe d'une formule à chaque fois que j'en ai besoin.)


7 Réponses :


6
votes

Celles-ci compileront probablement le même code de la machine, mais si cela compte vraiment, ça repère. Ou bien, mieux encore, utilisez simplement le Bitfield car il est plus facile!

Test rapidement des rendements GCC: P>

andl    $130023424, %edi     ; by-hand
shrl    $21, %edi
movl    %edi, %eax


4 commentaires

La version Bitfield est légèrement meilleure car elle utilise des constantes plus petites (1 octet au lieu de 4 octets), ce qui rend le code légèrement plus petit, économisant sur l'espace / impact de l'ICACHE


@Chris Dodd: True, mais je pense que c'est parce que GCC a organisé les champs différemment ...


@Chris Dodd: alors peut-être que les constantes peuvent être réduites en attribuant une gamme de caractères et des choses de casting sournoises? Par exemple, si j'avais un numéro de 11 bits, un numéro de 7 bits puis un numéro de 14 bits, je pouvais faire quelque chose comme: numéros de caractères [4]; first = ((Reterpret_cast (numéro) & 0xFFE0) >> 5); Deuxièmement = ((Reterpret_cast (numéro + 1) et 0x1fc0) >> 6); troisième = (réinterpret_cast (numéro + 2) et 0x03ff);


Oubliez le commentaire ci-dessus, je viens de réaliser que les devoirs dans les deux sens soient dangereusement dépendants de savoir si la machine est grande Endian ou peu d'Endian.



11
votes

Le compilateur génère les mêmes instructions que vous écririez explicitement pour accéder aux bits. Alors ne vous attendez pas à ce que ce soit plus rapide avec des champs de bit.

En fait, parlant strictement avec des champs de bit, vous ne contrôlez pas la manière dont ils sont positionnés dans le mot de données (à moins que votre compilateur vous donne des garanties supplémentaires. Je veux dire que la norme C99 ne définit aucun). Faire des masques à la main, vous pouvez au moins placer les deux champs les plus souvent accessibles d'abord et dernier dans la série, car dans ces deux positions, il faut une opération au lieu de deux pour isoler le champ.


4 commentaires

Il m'a fallu une minute pour comprendre pourquoi quelque chose dans les bits les plus importants serait plus rapide; Mais, c'est parce que vous n'avez besoin que de déplacer plutôt que du biole, puis de passer, est-ce que j'ai ce droit?


@James exactement. C'était seulement pour la lecture. J'ai réalisé que pour changer l'un des champs (laissant les autres de la même manière), si vous ne devez jamais faire cela, seul le champ le moins important a un avantage.


"Le compilateur génère les mêmes instructions que vous écririez explicitement pour accéder aux bits. Ne vous attendez donc pas à ce qu'elle soit plus rapide avec des champs de bit." - Ce n'est pas toujours vrai. Certains processeurs ont des instructions exactement pour cela, la manipulation avec des champs Bitfields, MC68020 + vient dans mon esprit. Ils ne sont pas toujours la solution la plus rapide, mais dans certains cas, ils sont.


@Miro: Dans ce cas, tout compilateur décent utiliserait ces instructions pour votre code écrit à la main pour accéder à des bits particuliers ...



1
votes

Seulement si votre architecture dispose explicitement d'un ensemble d'instructions pour la manipulation et l'accès à un bit-sage.


0 commentaires

0
votes

dépend. Si vous utilisez des champs de bits, vous laissez le compilateur à s'inquiéter de la manière de stocker les données (les champs Bitfields sont à peu complètement définis de la mise en œuvre), ce qui signifie que:

  • Cela pourrait utiliser plus d'espace que nécessaire et
  • L'accès à chaque membre sera effectué efficacement.

    Le compilateur organisera généralement la disposition de la structure de sorte que la deuxième hypothèse détient, au coût de la taille totale de la structure.

    Le compilateur insérera probablement un remplissage entre chaque membre, pour faciliter l'accès à chaque champ.

    D'autre part, si vous stockez tout dans un seul non signé long (ou une gamme de caractères), il vous appartient de mettre en œuvre un accès efficace, mais vous avez une garantie de la disposition. Cela prendra une taille fixe et il n'y aura pas de rembourrage. Et cela signifie que la copie de la valeur autour peut devenir moins chère. Et ce sera plus portable (supposer que vous utilisez un type INT de taille fixe au lieu de non signé INT ).


0 commentaires

4
votes

Dans cet écrit, j'utiliserais manuellement le champ Bit.
Mais pas à cause des accès. Mais à cause de la capacité de comparer deux dt.
En fin de compte, le compilateur générera toujours un meilleur code que vous (car le compilateur va devenir meilleur au fil du temps et ne jamais faire des erreurs), mais ce code est assez simple pour que vous écrivez probablement un code optimal (mais c'est le genre d'optimisation de la micro. ne pas vous inquiéter)

à l'aide d'une structure de champ de bit, le code ressemble à ceci: xxx

Bien sûr, vous pouvez obtenir le meilleur des deux mondes en utilisant un syndicat qui a la structure d'un côté et le Int comme alternative. Évidemment, cela dépendra exactement de la manière dont votre compilateur fonctionne et vous devrez tester que les objets sont placés dans les bonnes positions (mais ce serait un endroit parfait pour en apprendre davantage sur TDD.


5 commentaires

-1 Pour plusieurs raisons. Il n'est pas vrai qu'un compilateur générera toujours un meilleur code qu'un humain. Les compilateurs ne vont pas mieux au fil du temps. Même les plus petites optimisations peuvent importer dans certaines circonstances, en particulier dans le code intégré. C'est une mauvaise pratique pour que votre code dépend de la manière dont votre compilateur fonctionne, en particulier lorsque vous deviez le tester pour voir comment cela fonctionne.


@ Jeanne: 1) Alors que le compilateur ne fait jamais d'erreur, je trouve que c'est plus important que toute micro optimisation qu'un humain peut faire.


@ Jeanne: 2) Chaque fois qu'un humain figure en fait une optimisation macro. Il sera inclus dans la prochaine version du compilateur. Alors oui, les compilateurs deviennent mieux au fil du temps.


@ Jeanne: 3) Oui. Votre code ne devrait jamais dépendre des dépendances du compilateur. Et ma suggestion ne le fait pas. Mais ce serait bien pour un nouvel utilisateur de le tester afin qu'il comprenne pourquoi!


@eanne: 4) TDD est un moyen assez standard de développer un code bon robuste. Suggérant que c'est une mauvaise idée de tester est juste idiot même si vous êtes sûr que ce qui est correct, le test est toujours une idée de Dieu.



4
votes

C'est entièrement plate-forme et compilateur dépendante. Certains processeurs, en particulier les microcontrôleurs, ont des instructions d'adresses de bits ou de mémoire adressable au bit, et le compilateur peut les utiliser directement si vous utilisez des constructions de langue intégrées. Si vous utilisez un masquage bit pour fonctionner sur des bits sur un tel processeur, le compilateur devra être plus intelligent pour repérer l'optimisation potentielle.

sur la plupart des plates-formes de bureau, je suggérerais que vous transpiriez les petites choses, mais si vous avez besoin de savoir, vous devez le tester en profilant ou en califiant le code ou analysez le code généré. Notez que vous pouvez obtenir des résultats très différents en fonction des options d'optimisation du compilateur et même des compilateurs différents.


0 commentaires

1
votes

Le compilateur peut parfois combiner l'accès aux champignons dans une matière non intuitive. J'ai une fois désassemblé le code généré (GCC 3.4.6 pour SPARC) lors de l'accès à 1 bits entrées qui utilisées dans une expressions conditionnelles. Le compilateur a fusionné l'accès aux bits et a fait la comparaison avec des entiers. Je vais essayer de reproduire l'idée (pas au travail et je ne peux pas accéder au code source impliqué): xxx

a été compilé à quelque chose d'équivalent à (je sais que le Bitorder dans mon exemple est le en face mais ce n'est que pour la simplification de l'exemple). xxx

Il existe également un autre point à considérer si on veut utiliser des champs de bit (ils sont souvent plus lisibles que le bit -Kungfu), si vous avez des bits à épargner, il peut être utile de réserver des octets entiers pour certains domaines, ce qui simplifie ainsi l'accès par le processeur. Un champ 8 bits aligné peut être chargé avec une commande d'octet de déplacement et n'a pas besoin de l'étape de masquage.


1 commentaires

Oui et quand les gens bit-kungfu, il est beaucoup plus maintenu à utiliser #define pour des positions de bits et / ou des masques qui rappelle à d'autres personnes ou à votre avenir, ce que vous avez fait. Si vous avez besoin de le faire dans plus d'un endroit, des macros pour la création de masque à partir de la position du bit et de la largeur sont utiles. par exemple. #define ExtractBitfield (valeur, LSB, largeur) ((valeur >> LSB) & ((1 << largeur) -1))