31 votes

Pourquoi ne compilateur inline produire plus lent que le code manuel inline?

Arrière-plan

La critique suivante de la boucle d'un morceau de numérique logiciel, écrit en C++, essentiellement compare deux objets par un de leurs membres:

for(int j=n;--j>0;)
    asd[j%16]=a.e<b.e;

a et b sont de la classe ASD:

struct ASD  {
    float e;
    ...
};

J'ai été d'étudier l'effet de mettre cette comparaison dans un léger fonction de membre:

bool test(const ASD& y)const {
    return e<y.e;
}

et de l'utiliser comme ceci:

for(int j=n;--j>0;)
    asd[j%16]=a.test(b);

Le compilateur est inline cette fonction, mais le problème, c'est que le code assembleur sera différente et la cause >10% de gestion d'exécution. J'ai à la question:

Questions

  1. Pourquoi le compilateur prodrucing différentes assemblée de code?

  2. Pourquoi est-ce le produit d'assemblage plus lent?

EDIT: La deuxième question a été répondu par la mise en œuvre de @KamyarSouri de la suggestion (j%16). L'assemblée actuellement, le code est presque identique (voir http://pastebin.com/diff.php?i=yqXedtPm). Les seules différences sont les lignes 18, 33, 48:

000646F9  movzx       edx,dl 

Matériel

Ce graphique montre le FLOP/s (jusqu'à un facteur d'échelle) pour 50 lancement pour test de mon code.

enter image description here

Le script gnuplot pour générer le tracé: http://pastebin.com/8amNqya7

Options Du Compilateur:

/Zi /W3 /WX- /MP /Ox /Ob2 /Oi /Ot /Oy /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /MT /GS /Gy /arch:SSE2 /fp:précis /Zc:wchar_t /Zc:forScope /Gd /analyser-

Options Du Linker: /INCRÉMENTAL:PAS de "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /ALLOWISOLATION /MANIFESTUAC:"niveau='asInvoker' uiAccess='false'" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:FILE d'attente

31voto

Mysticial Points 180300

Réponse Courte:

Votre asd tableau est déclaré comme ceci:

int *asd=new int[16];

Par conséquent, utilisez int que le type de retour plutôt que d' bool.
Vous pouvez également modifier le type de tableau d' bool.

En tout cas, le type de retour de la test de la fonction correspondant au type de la matrice.

Sautez vers le bas pour plus de détails.

Réponse Longue:

Dans le manuellement inline version, le "noyau" d'une itération ressemble à ceci:

xor         eax,eax  

mov         edx,ecx  
and         edx,0Fh  
mov         dword ptr [ebp+edx*4],eax  
mov         eax,dword ptr [esp+1Ch]  
movss       xmm0,dword ptr [eax]  
movss       xmm1,dword ptr [edi]  
cvtps2pd    xmm0,xmm0  
cvtps2pd    xmm1,xmm1  
comisd      xmm1,xmm0  

Le compilateur inline version est totalement identiques à l'exception de la première instruction.

Où, au lieu de:

xor         eax,eax

il a:

xor         eax,eax  
movzx       edx,al

Ok, donc c'est un supplément d'instruction. Ils font tous les deux la même mise à zéro d'un registre. C'est la seule différence que je vois...

L' movzx instruction a un seul cycle de latence et d' 0.33 cycle réciproque débit sur toutes les nouvelles architectures. Donc je ne peux pas imaginer comment cela pourrait faire une différence de 10%.

Dans les deux cas, le résultat de la réduction à zéro est utilisé que 3 des instructions plus tard. Il est donc très possible que cela pourrait être sur le chemin critique de l'exécution.


Alors que je ne suis pas un Intel ingénieur, voici mon avis:

La plupart des processeurs modernes traitent de la réinitialisation des opérations ( xor eax,eax) via le registre de renommage à une banque de zéro des registres. Il ignore totalement les unités d'exécution. Cependant, il est possible que cette manipulation pourrait causer un pipeline de la bulle lors de l' (partielle) le registre est accessible via movzx edi,al.

En outre, il y a aussi un faux dépendance de l' eax dans le compilateur inline version:

movzx       edx,al  
mov         eax,ecx  //  False dependency on "eax".

Si oui ou non le hors-de-exécution de l'ordre est en mesure de résoudre ce est au-delà de moi.


Bon d'accord, c'est fondamentalement transformer en une question de reverse-engineering de la MSVC compilateur...

Ici, je vais vous expliquer pourquoi que les extra - movzx est généré en tant que bien que pourquoi il reste.

La clé ici est l' bool de la valeur de retour. Apparemment, bool types de données sont probablement stocké sur 8 bits des valeurs à l'intérieur de la MSVC interne-représentation. Par conséquent, lorsque vous convertir implicitement d' bool de int ici:

asd[j%16] = a.test(b);
^^^^^^^^^   ^^^^^^^^^
 type int   type bool

il y a un 8-bits -> entier de 32 bits de promotion. C'est la raison pour laquelle MSVC génère l' movzx enseignement.

Lorsque l'alignement est effectué manuellement, le compilateur dispose de suffisamment d'informations pour optimiser cette conversion et maintient le tout en 32 bits type de données IR.

Toutefois, lorsque le code est mis dans une fonction avec un bool de la valeur de retour, le compilateur n'est pas capable d'optimiser les 8 bits de l'intermédiaire de type de données. Par conséquent, l' movzx des séjours.

Quand vous faites deux types de données identiques (soit int ou bool), aucune conversion n'est nécessaire. D'où le problème est évité complètement.

1voto

Windows programmer Points 5365

lea esp,[esp] occupe 7 octets de je-cache et c'est à l'intérieur de la boucle. Quelques autres indices font ressembler le compilateur n'est pas sûr si c'est une version release ou debug.

Edit:

L' lea esp,[esp] n'est pas dans la boucle. La position parmi l'entourage des instructions a me tromper. Maintenant, on dirait qu'il intentionnellement gaspillage de 7 octets, suivi par un autre gaspillage de 2 octets, afin de lancer le réel de la boucle à un 16-frontière d'octet. Ce qui signifie que cela accélère les choses, comme le fait observer Johennes Gerer.

Le compilateur semble encore incertain si c'est un debug ou release construire bien.

Une autre édition:

Le pastebin diff est différente de la pastebin diff que j'ai vu auparavant. Cette réponse pourrait être supprimé maintenant, mais il a déjà des commentaires donc je vais le laisser.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X