Pourquoi le compilateur génère-t-il des sqrts supplémentaires dans le code d’assembly compilé?

J’essaie de définir le temps nécessaire pour calculer un sqrt à l’aide du code C simple suivant, où readTSC () est une fonction permettant de lire le compteur de cycles de la CPU.

double sum = 0.0; int i; tm = readTSC(); for ( i = 0; i < n; i++ ) sum += sqrt((double) i); tm = readTSC() - tm; printf("%lld clocks in total\n",tm); printf("%15.6e\n",sum); 

Cependant, comme j’ai imprimé le code d’assemblage en utilisant

 gcc -S timing.c -o timing.s 

sur une machine Intel, le résultat (illustré ci-dessous) a été surprenant?

Pourquoi le code d’assemblage contient-il deux sqrts, l’un utilisant l’instruction sqrtsd et l’autre utilisant un appel de fonction? Est-ce lié au déroulement de la boucle et à l’exécution de deux sqrts en une itération?

Et comment comprendre la ligne

 ucomisd %xmm0, %xmm0 

Pourquoi compare-t-il %xmm0 à lui-même?

 //----------------start of for loop---------------- call readTSC movq %rax, -32(%rbp) movl $0, -4(%rbp) jmp .L4 .L6: cvtsi2sd -4(%rbp), %xmm1 // 1. use sqrtsd instruction sqrtsd %xmm1, %xmm0 ucomisd %xmm0, %xmm0 jp .L8 je .L5 .L8: movapd %xmm1, %xmm0 // 2. use C funciton call call sqrt .L5: movsd -16(%rbp), %xmm1 addsd %xmm1, %xmm0 movsd %xmm0, -16(%rbp) addl $1, -4(%rbp) .L4: movl -4(%rbp), %eax cmpl -36(%rbp), %eax jl .L6 //----------------end of for loop---------------- call readTSC 

Il utilise la fonction sqrt la bibliothèque pour la gestion des erreurs. Reportez-vous à la documentation de glibc: 20.5.4 Rapport d’erreurs par fonctions mathématiques : les fonctions mathématiques définissent errno avec un errno de compatibilité avec les systèmes dépourvus d’indicateurs d’exception IEEE754. Connexe: la math_error(7) de math_error(7) glibc.

En sqrtsd optimisation, il essaie d’abord d’exécuter la racine carrée avec l’instruction sqrtsd , puis vérifie le résultat par rapport à lui-même à l’aide de l’instruction ucomisd qui définit les indicateurs comme suit:

 CASE (RESULT) OF UNORDERED: ZF,PF,CF 111; GREATER_THAN: ZF,PF,CF 000; LESS_THAN: ZF,PF,CF 001; EQUAL: ZF,PF,CF 100; ESAC; 

En particulier, comparer un QNaN à lui-même renverra UNORDERED , ce que vous obtiendrez si vous essayez de prendre la racine carrée d’un nombre négatif. Ceci est couvert par la twig jp . Le je check est juste de la paranoïa, vérifiant l’égalité exacte.


Notez également que gcc a une -fno-math-errno qui sacrifie cette gestion des erreurs pour plus de rapidité. Cette option fait partie de -ffast-math , mais peut être utilisée seule sans activer les optimisations modifiant les résultats.

sqrtsd produit correctement NaN pour les entrées négatives et NaN, et définit l’indicateur IEEE754 Invalid. La vérification et la twig servent uniquement à préserver la sémantique de définition d’erreur, sur laquelle la plupart des codes ne s’appuient pas.

-fno-math-errno est la valeur par défaut de Darwin (OS X), où la bibliothèque mathématique ne définit jamais errno , les fonctions peuvent donc être insérées sans cette vérification.