Comparaison plus rapide des signatures signées que des signatures non signées

Dupliquer possible:
performance des entiers non signés vs signés

J’ai lu quelque part qu’il est un peu plus rapide sur x86_64 de comparer les signed ints en C/C++ par rapport aux unsigned ints , par exemple, for (int i... ), il est “plus rapide” que for (uint i...) .

Est-ce vrai? Pourquoi est-ce vrai? Je sais que la différence est super petite, mais quand même.

Vous feriez mieux de citer une source pour une telle affirmation, ce qui est ridicule à première vue.

Nous parlons de x86_64, ce qui signifie processeurs modernes. Ces ALU complèteront l’addition, la soustraction et / ou la comparaison d’entiers en un seul cycle d’horloge (les erreurs de mémoire cache prendront plus de temps, mais ne dépendent que de la taille et de la disposition de la mémoire des données, et non de la signature). Ou encore moins, avec le coprocesseur SIMD.

J’aurais plus de chances de croire qu’il existe une légère différence de puissance entre les deux types de comparaison, mais pas une différence de vitesse.

À présent, il est possible que pour un compilateur particulier, la génération de code soit pire pour un type de données que pour l’autre lors du ciblage de la plate-forme x86_64. Mais ce serait un cas très spécialisé et peu susceptible de s’appliquer à tous les compilateurs x86_64. Et pourtant, je soupçonnerais que des effets de cache ou des processus en arrière-plan affectaient la mesure de la performance (même les compteurs de performance qui mesurent le temps passé par processus seront affectés par un changement de contexte qui invalide les caches).

Eh bien, il n’y a pas de bonne réponse ici. Les différences de vitesse sont généralement si marginales que vous obtenez de meilleures performances en passant votre temps à penser à autre chose. Mais il existe quelques cas étranges, car le débordement signé n’est pas défini. Par exemple, comparez ces deux:

 for (int i = 0; condition(); ++i) { if (i == 0) { computation(); } } for (unsigned i = 0; condition(); ++i) { if (i == 0) { computation(); } } 

Un compilateur conforme peut déplacer le computation dehors de la boucle qui utilise un index signé, car il peut supposer que i == 0 une et une seule fois, car le dépassement du nombre de signatures est un comportement indéfini (le dépassement de capacité pourrait mettre fin au programme, ou en faire tourner les démons, ou par le nez). Cependant, le compilateur ne peut pas déplacer le computation dehors de la seconde boucle sans beaucoup de travail (les entiers non signés sont toujours bouclés quand ils débordent).

Cependant, sur x86_64, vous devez souvent signer-étendre un int avant de travailler avec celui-ci. Cela prend une instruction supplémentaire.

Conclusion: ce n’est pas vraiment important. Celui qui vous a dit que l’un est plus rapide que l’autre vous empêche de restr productif.

Peut-être que dans certains cas, les entiers signés obligent le compilateur à les signer.
Si vous déplacez un entier sur eax , les bits hauts de rax sont mis à rax (les bits bas sont les bits de eax ). S’il s’agit d’un entier, vous devez définir les 32 bits supérieurs sur le signe des 32 bits inférieurs. C’est une instruction supplémentaire.

Je ne suis pas sûr que cette extension de signe soit nécessaire de manière simple for (i=0; i .

Il ne devrait y avoir aucune différence de vitesse. L’instruction de comparaison (CMP) sur x86 32/64 est la même pour les types de données entiers signés et non signés. Toutes les instructions de twigment (jxx) et les déplacements conditionnels (cmovxx) fonctionnent en testant les indicateurs de processeur modifiés par CMP. Les incréments, les décréments, les dépendances, les soustractions sur les données entières ne sont pas non plus conscients des types de données signés ou non signés (en raison du complément à 2).

Mesure

La mesure sur la plate-forme a toujours été la seule méthode pour répondre à ce type de questions (et x86_64 n’est qu’un sharepoint départ pour déterminer la plate-forme, le modèle précis peut être nécessaire). De nos jours, les optimisations effectuées sur la puce (regardez ce que signifie “processeur superscalar avec ordre d’exécution spéculative”) et par les compilateurs font ce genre de différences (en supposant qu’elles existent, ce que je ne fais pas vraiment pour les comparaisons signées / non signées, mais des optimisations telles que la propagation de la plage de valeurs et la réduction de la résistance peuvent avoir une influence), de sorte que le contexte dépend du fait qu’une mesure dans le contexte souhaité est nécessaire car elle aura une influence sur le résultat (j’ai eu du code dans lequel certaines variables entières sont exécutées deux fois mieux sur certaines machines, pire sur d’autres).