Opérateurs binarys, pas vs utilisation xor dans les twigs

Après avoir posé cette question SO , j’ai reçu un commentaire très intéressant de @ AndonM.Coleman que je devais vérifier.

Étant donné que votre code désassemblé est écrit pour x86, il convient de souligner que XOR définira / effacera l’indicateur zéro alors que NON ne le sera pas (utile si vous souhaitez effectuer une opération au niveau du bit sans affecter les conditions de saut qui reposent sur des indicateurs d’opérations précédentes) . Maintenant, étant donné que vous n’écrivez pas directement à l’assemblée, vous n’avez vraiment pas access à ce drapeau de manière significative, alors je doute que ce soit la raison pour laquelle vous favorisez l’un sur l’autre.

Son commentaire m’a demandé si le code suivant produirait les mêmes instructions d’assemblage

#include  int main() { unsigned int val = 0; std::cout <> val; if ( (val ^ ~0U) == 0) { std::cout << "Value inverted is zero" << std::endl; } else { std::cout << "Value inverted is not zero" << std::endl; } if ( (~val) == 0) { std::cout << "Value inverted is zero" << std::endl; } else { std::cout << "Value inverted is not zero" << std::endl; } return 0; } 

Pour les deux opérations suivantes

 if ( (val ^ ~0U) == 0 ) 

et

 if ( (~val) == 0 ) 

La version non optimisée de Visual Studio 2010 donne le désassemblage suivant:

  if ( (val ^ ~0U) == 0) 00AD1501 mov eax,dword ptr [val] 00AD1504 xor eax,0FFFFFFFFh 00AD1507 jne main+86h (0AD1536h) if ( (~val) == 0) 00AD1561 mov eax,dword ptr [val] 00AD1564 not eax 00AD1566 test eax,eax 00AD1568 jne main+0E7h (0AD1597h) 

Ma question concerne l’optimisation. Est-il préférable d’écrire

 if ( (val ^ ~0U) == 0) 

ou

 if ( (~val) == 0) 

    Cela dépend de beaucoup de choses, mais surtout de ce que vous demandez éventuellement au compilateur d’optimiser.

    Si le compilateur est configuré pour optimiser la taille (le plus petit bytecode), il utilisera parfois XOR dans des endroits apparemment étranges. Par exemple, le schéma de codage à longueur variable utilisé par X86 peut définir un registre sur 0 en XOR lui-même le nombre d’octets de code requirejs par l’instruction MOV .

    Considérons le code qui utilise XOR :

     if ( (val ^ ~0U) == 0 ) /* 3-bytes to negate and test (x86) */ 

    XOR eax,0FFFFFFFFh nécessite 3 octets ET XOR eax,0FFFFFFFFh / efface l’ XOR eax,0FFFFFFFFh zéro (ZF)

    Maintenant, considérons le code qui utilise NOT :

     if ( (~val) == 0) /* 4-bytes to negate and test (x86) */ 

    NOT eax est codé dans une instruction de 2 octets, mais n’affecte pas les indicateurs de la CPU.

    TEST eax,eax ajoute 2 octets supplémentaires et est nécessaire pour définir / effacer l’indicateur zéro (ZF)

    NOT est également une instruction simple, mais comme cela n’affecte aucun indicateur d’UC, vous devez ensuite émettre une instruction TEST pour pouvoir l’utiliser comme twig dans le code. Cela produit en fait un plus grand code d’octet. Par conséquent, un compilateur intelligent à optimiser pour la taille essaiera probablement d’éviter d’utiliser NOT . Le nombre total de cycles nécessaires à l’exécution de ces instructions varie selon la génération de CPU, et un compilateur intelligent en tiendra compte également dans sa prise de décision lorsqu’il lui sera demandé d’optimiser sa vitesse.


    Si vous n’écrivez pas un assemblage réglé à la main, il est préférable d’utiliser tout ce qui est le plus clair pour un humain et d’espérer que le compilateur soit suffisamment intelligent pour choisir différentes instructions / ordonnancement / etc. pour optimiser la taille / la vitesse demandée au moment de la compilation. Les compilateurs disposent d’un ensemble intelligent d’heuristiques pour choisir et planifier leurs instructions. Ils en savent plus sur l’architecture de la CPU cible que le codeur moyen.

    Si vous découvrez plus tard que cette twig est réellement un goulot d’étranglement et qu’il n’existe pas de solution plus globale au problème, vous pouvez effectuer un réglage de bas niveau. Cependant, c’est une chose tellement sortingviale sur laquelle il faut se concentrer de nos jours, sauf si vous ciblez quelque chose comme un processeur intégré à faible consommation d’énergie ou un périphérique limité en mémoire. Les seuls endroits où j’ai obtenu suffisamment de performances pour le rendre rentable étaient les algorithmes bénéficiant du parallélisme des données et où le compilateur n’était pas assez intelligent pour utiliser efficacement des jeux d’instructions spécialisés tels que MMX / SSE.